• Мысленный эксперимент: Flutter на Go
    0
    Поясните, пожалуйста, что вы имеете в виду.

    Я придираюсь к другой теме (100500 возможностей написать одно и тоже), но у вас ArrayList, а в другой версии – обычный array. Чтобы их использовать между собой, придётся конвертировать из одного в другой, верно?


    а не потому что дженерики ухудшают читабельность в общем случае.

    В моей картине мира, это взаимосвязано. Дженерики нередко приводят к решениям, которые бы в отсутствие оных было бы гораздо проще и прагматичнее. Они как бы говорят – смотри, ты задизайнишь тип под все возможные варианты – даже там где это в принципе не возможно, или в принципе не нужно. У меня, видимо, от дженериков психологическая травма после 10 лет работы с магией темплейтов в С++ и программистов, свято верящих, что чем больше обобщения в типах, тем лучше.


    Я плохо ориентируюсь в исходниках го, но убежден что там тоже полно кода, на понятие которого требуется время

    К такой формулировке сложно придраться, но если откровенно, то нет – исходники стандартной библиотеки в Go это образцовый и идиоматический Go код, на котором, кстати, рекомендуется учиться. Я не знаю ни одного другого языка, где можно гордо советовать читать код стандартной библиотеки новичкам, чтобы учить сам язык. И это неспроста.

  • Мысленный эксперимент: Flutter на Go
    0
    Прочитать абстрактный набор данных, выбрать из него только лишь необходимое, обработать и передать дальше — вы ведь согласны что это чуть ли не самый частый кейс в программировании?

    Да, и он называется "цикл". Если я вас правильно понял, вы считаете конструкцию for ... range data {} бойлерплейтом, а data.map(...) - красивым минималистичным кодом? Но я не вижу в этом настолько фундаментальной проблемы, чтобы оправдывать существенное усложнение языка – например такое, которое сейчас рассматривается как стартовый черновик пропозала дженериков для Go 2.

  • Мысленный эксперимент: Flutter на Go
    –1

    Забавно, как ваши Java-реализации несовместимы друг с другом без приведения типов :D


    Теперь смотрите – на практике необходимость применить какую-то функцию на массив данных возникает периодически и элементарно решается средствами языка – вот таким вот простым циклом. Более, того, ещё и даёт больше гибкости – хотите, новый массив создавайте, хотите – прямо in-place меняйте и т.д.


    Но нет, в попытке обобщить (DRY! цикл писать два раза – зло!) мы создаём (есть же дженерики, значит надо всё дженерилизовать!) реализацию map(), придумав для этого новую концепцию Streams – в которую теперь программисты будут бездумно запихивать массив (потому что это Java-way). При этом сама реализация на порядок сложнее и нечитабельней, чем те несовместимые примеры, которые вы привели выше.


    Это действительно уменьшит код с 3-х строчек до 1-й, но какой ценой? Ценой привнесения в язык дополнительных килобайт универсального-генерализованного-под-все-случаи-жизни кода – причем кода, который сложно даже увидеть (у меня заняло минут 20 пробиться через толщи абстракций до файле ReferencePipeline.java, в котором находится реализация map).


    И всё ради того, чтобы не писать три строчки цикла.


    Извините, но кроме очень специфических пайплайнов стрим-процессесинговых систем, такой подход – это ничем не оправданная сложность, когнитивная и процессинговая, и это именно то, с чем вся индустрия должна бороться, а не поощрять. В этом мы, конечно, расходимся.

  • Мысленный эксперимент: Flutter на Go
    +1
    то я не могу написать универсальную структуру — не важно список, дерево или что-то еще — которую смогу переиспользовать типобезопасным образом и без бойлерплейта. Проблема именно в этом, а не в чем-то другом, так что давайте обойдемся без пассивно-агрессивных нападок на мой уровень программирования, хорошо?

    Окей, резонно, прошу прощения. Но мой поинт был в том, что если вам нужно обработать конкретный json и передать дальше – вам не нужно писать "универсальную структуру" – это принципиально разные задачи. Из ваших слов я понял, что вы видите потребность в дженериках даже для таких простых случаев, и связал это с давно наблюдаемым эффектом переиспользования дженериков по поводу и без повода – как раз из-за отстутствия чего Go и выигрывает в читабельности и понятности.


    Если зайти к тому же джава-комьюнити и сказать что ты ждешь добавления элвис-оператора или интерполяции строк — тебе никто не скажет,

    А в C++ коммьюнити вас ещё и на руках будут носить за предложение новых фич. В Go ж как раз это главное отличие – жесткая позиция по поводу необходимости новых фич – так что непонятно, к чему этот пример.


    В общем подобное разворачивание хорошо знакомого метода в мозгу — это обычный приобретаемый навык, который оттачивается до рефлексов.

    Вы правы, но мы тут говорим о двух разных вещах. Первая – это сколько "разворачивания" перекладывать на код, и сколько на мозг. Функциональные языки вроде Haskell, например, тяготеют к тому, что сначала нужно много всякого "загрузить" в мозг, чтобы потом максимально короткими языковыми конструкциями можно было выразить максимум. Они прям прутся от этого и считают это благим намерением и самоцелью. Я никогда это не понимал, и для меня это ровная противоположность "ясности" и "читабельности".
    Вторая – это сколько redundancy в конструкциях должно присутствовать в языке. Если map можно реализовать уже – зачем его добавлять в язык? Потому что вы считаете, что так сделаете код лучше? А другие так не считают. Go не пытается предсказать, как лучше – даёт в руки минимум, на котором можно построить любой из вариантов – хотите map/filter, сделайте себе и пользуйтесь. Не хотите – не пользуйтесь, язык не навязывать. Опять – чем проще язык, тем он гибче и мощнее – что позволяет фокусироваться не на пользовании языком ("а что я должен использовать – цикл или map?"). а на решении бизнес задачи.

  • Мысленный эксперимент: Flutter на Go
    +1
    А теперь объясните, каким образом второй пример кода у вас вдруг оказался нечитаемым и медленным.

    mayorovp, да элементарно, дженерики же не только эту строчку в коде меняют ) Вот смотрите, ваши же примеры, но мы смотрим на код map() (который ведь тоже код, который должны люди, особенно ежедневно пишущие свои собственные структуры данных):


    Go:


    func Map(in []string, fn func(string)string) []string {
        out := make([]string, len(in))
        for i, val := range in {
            out[i] = fn(val)
        }
        return out
    }

    Java:


    @Override
        @SuppressWarnings("unchecked")
        public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
            Objects.requireNonNull(mapper);
            return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE,
                                         StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
                @Override
                Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) {
                    return new Sink.ChainedReference<P_OUT, R>(sink) {
                        @Override
                        public void accept(P_OUT u) {
                            downstream.accept(mapper.apply(u));
                        }
                    };
                }
            };
        }

    и это, конечно, без кода тех классов, которые используются внутри.


    Извините, но написать одно приведение типа, или реализовать трёхстрочник и, при необходимости, нужный тип в нём вставить (ручками или кодогенератором) это всё таки в чём-то лучше, чем порождать вот такой нечитабельный код с титаническим количеством ненужной магии в каждой строчке кода.

  • Мысленный эксперимент: Flutter на Go
    +1
    Лично на мой взгляд это на столько специфичные вещи, необходимость в которых возникает очень редко, однако они добавлены в язык. А дженерики — нет.

    Хороший пример. Разница в том, что добавление iota практически ничего не стоит (это ортогональный концепт, который не влияет ни на что другое), а все варианты дженериков, которые много лет анализировались и с которыми экспериментировали (и продолжают) – радикально усложняют язык, код, ухудшают и замедляют опыт работы с ним. Если бы добавить дженерики было бы также легко и безболезненно, как и iota, то дженерики бы в Go были с самого начала. Это же техническое решение было, а не политическое.


    получить от внешнего сервиса коллекцию объектов, обработать их, трансформировать в нужный вид и передать дальше

    Для такого дженерики вообще не нужны ни разу. Вы сейчас хорошо показали, в чём проблема – начинается с "я хочу писать реюзабельные структуры данных", а заканчивается "я не могу массив из json обработать без дженериков". И это проблема :D


    И видя в коде цепочку filter().map().collect()

    Понимаешь, что там три вложенных цикла (разворачиваешь бойлерплейт у себя в голове, что есть дополнительной когнитивной нагрузкой). Или не понимаешь, конечно – и лепишь монстроидальные однострочные конструкции .map.filter.map.collect..., а потом удивляешься, почему всё так медленно работает.


    Опять же, подход Go — реализовать в языке минимально необоходимый набор фич, из которых можно построить все остальные. Если что-то можно сделать уже существующими фичами – то добавлять это в язык не стоит.


    Многие разработчики языков считают, что наилучший способ сделать гибкий язык – это предсказать как можно больше кейсов и задизайнить под них фичи. Но это создаёт тонну дополнительной сложности и когнитивного оверхеда. Подход Go – дать минимум самых необходимых фич из которых можно создать все остальные, и сделать код максимально легким для изменения и рефакторинга. Как бы мне хотелось, чтобы другие языки осознали эту истину.

  • Мысленный эксперимент: Flutter на Go
    +1

    Я вас понял. Да, это ортогональные вопросы (вынесение стейта в отдельную сущность для того, чтобы конструктор виджета можно было использовать в построении дерева виджетов и конкретный дизайн типа с дженериками или без).

  • Мысленный эксперимент: Flutter на Go
    0
    т.к. мне постоянно приходиться работать с коллекциями в том или ином их виде (как и ооочень большой пласт программистов, я думаю) и я не хочу постоянно писать циклы для фильтрации\модификации списков. А golang не позволяет мне вынести весь этот бойлерплейт в хелпер-методы, сохранив при этом безопасность работы с типами.

    Всё Go позволяет, просто вы пока мыслите на языке Java, а писать пытаетесь на Go (судя даже по формулировке задачи – "коллекции", "фильтрации списков" и т.д.). На самом деле вы, конечно же, совершенно валидную проблему описываете, но давайте я уточню, правильно ли я понял – вы говорите, что большую часть вашего ежедневного кода составляют а) нестандартные для Go структуры данных б) операции над массивами map/filter/reduce и вам не хочется писать их в виде циклов (чем они, по сути и являются) и вы не видите способ написать их на Go. Я верно понял суть проблемы?


    И тут, мне кажется, мы снова приходим к вопросу о частоте использования обсуждаемых кейсов. Вот мне хотелось бы максимально честный ответ на вопрос:


    • как часто вам приходится выбирать структуру данных, которой нет из коробки в Go — скажем, red-black tree? (интересует конкретное число – там, 3 раза в день, или 10 раз в месяц)

    Второй вопрос по поводу оформления map во враппер – это, конечно же делается – блин, это буквально циклы, их писать 15 секунд и вероятность ошибиться 0.0001%. Это пишется быстрее, чем комментарий о том, как сложно жить без дженериков:


    func Map(in []string, fn func(string)string) []string {
        out := make([]string, len(in))
    
        for i, val := range in {
            out[i] = fn(val)
        }
    
        return out
    }
    ...
    in := []string{"1234", "sadd", "3434"}
    out := Map(in, func(s string) string {
        return s + " mapped"
    })

    Если вы совсем уж уверены, что у вас такой специфический кейс, что в каждой программы вам нужно сотни раз делать map/filter/reduce на 100 разных типов в каждой строке, то вариант с интерфейсами пишется один раз на всю жизнь, и дальше единственное отличие от привычного вам в том, что нужно привести тип один раз. Давайте, чтобы вам было проще понять фундаментальную разницу (точнее отсутствие оной), я переименую interface{} в Object:


    type Object = interface{} // don't do this outside of Habr examples
    type mapf func(Object) Object
    
    func Map(in Object, fn mapf) Object {
        val := reflect.ValueOf(in)
        out := make([]Object, val.Len())
    
        for i := 0; i < val.Len(); i++ {
            out[i] = fn(val.Index(i).Interface())
        }
    
        return out
    }
    ...
    in := []string{"1234", "sadd", "3434"}
    out := Map(in, func(s Object) Object {
        return s.(string) + " mapped"
    })

    И я вас прекрасно понимаю – если вам приходится map использовать сотни раз в день на все типы, то подход Go будет казаться многословным. Но я из головы могу придумать только один вариант, когда это будет реальностью – "лабораторные по информатике", на которых люди учат map/reduce/filter. В практической разработке – это либо неправильно выбранный инструмент (может вам R нужен и вы тупо данные молотите), либо это сильно преувеличенная потребность.


    Учитывая то. что эта проблема элементарно решается, и у нее масса обходных путей решения, Go действительно спокойно цветет без дженериков. Которые авторы и коммьюнити и не против бы ввести, но без того, чтобы превращать язык в нечитабельного и медленного монстра, как почти все остальные языки – и такого способа пока не придумали, что не случайно.


    Зато от отсутствия дженериков сильно выигрывает читабельность кода и отсутствие монстроидальных дизайнов, и вот эту фишку очень сложно перебить выгодой от "не нужно писать три строчки цикла".

  • Мысленный эксперимент: Flutter на Go
    0
    Мы как-то сильно разошлись. Я имел ввиду, что это требование «title – свойство, counter — состояние» вы откуда-то сами принесли ) Но я бы не сильно налегал тут, потому что снова же – при любом малейшем изменении требований, «свойство» легко превращается в «состояние», и фундаментального отличия между ними нет. Это просто код по разному пытается на этих отличиях оптимизировать внутренные процессы отрисовки и менеджмента стейта.
  • Мысленный эксперимент: Flutter на Go
    0

    Сразу прокомментирую вот этот момент — "у title и counter разная природа": для меня это был сюрприз, потому что в моём понимании нет никакого ограничения, почему бы виджет сам не мог изменить себе title.


    Я прекрасно понимаю подход Flutter. "Стейт это и есть виджет" это как раз то, что я пытаюсь объяснить – у нас и так уже есть у каждого виджета "стейт" (поля класса). Новая сущность "стейт" – это уже другая сущность и в моём понимании, на каждую сущность в ментальной модели (в голове) должна приходится одна сущность (тип) в коде. Плодить пачку типов для одной сущности – это признак какой-то путаницы в голове, и самый простой способ сделать код малопонятным и малочитаемым. Вот то, что я упоминал в статье про "зачем мы метод build() определяем не на виджет, а на стейт" – это имеет мало смысла, если не понимать, что всё это пляски вокруг дизайна.

  • Мысленный эксперимент: Flutter на Go
    0
    Но дженерики тут ни при чём, виной тому — неудачные наименования. Скажите, если Widget обозвать WidgetProps, а State — WidgetImpl, это починит вашу ментальную модель?

    Нет. Моя (и, полагаю, ваша тоже) ментальная модель это "виджет", у которого есть или нет "свойства", которые влияют на отображение. Чем лучше код "маппится" на ментальную модель, тем он проще, лучше и понятней. WidgetProps и WidgetImpl звучат как хаки вокруг дизайна языка программирования, а не как попытка смаппить проблемную область на код.
    Я понимаю ваш подход, но это мой личный pet peeve – на моей практике такой код при любом следующем изменении в реальной задаче (например, много виджетов будут шерить один стейт) уже не будет поддаваться гармоническому рефакторингу и будет порождать всё более ужасные конструкции (GroupedCoreStatePropsWidget?).

  • Мысленный эксперимент: Flutter на Go
    0

    Мне кажется, вы оба правы, и про дженерики все споры упираются в то, насколько важны те или иные кейсы.


    Люди, которые пишут структуры данных ежедневно, действительно не представляют жизнь без дженериков – но на моём опыте, это либо студенты на лабораторных занятиях, либо очень узкоспециализированные разработчики из PLT research тусовки. Большинство же практических задач, действительно, не требуют ежедневно писать новые универсальные структуры данных, и почти всегда работают с конкретными типами


    Именно поэтому Go так и выстрелил, несмотря на отсутствие пользовательских дженериков – встроенных дженериков и интерфейсов достаточно для большинства задач, а вышеописанные кейсы решаются либо пустыми интерфейсами (не очень красиво и не супер-быстро), либо копипастой (ручной или автогенератором) – что, иронически, фундаментально не сильно отличается от реализации дженериков в других языках (только там это под капотом). Вобщем, в Go есть workaround-ы, и, похоже, что их достаточно в 90% случаев.


    Зло от дженериков в том, что они дают опасные надежды на то, что можно не сильно утруждаться дизайном типов данных, и дают ложное ощущение гибкости – которое зачастую порождает монстроидальные дизайны, которые сложно понимать, поддерживать и рефакторить (в комментариях ниже есть пример). Кроме того, в разработке фокус смещается на код, а не на данные ("я хочу писать код, который работает с любыми типами") – что в 99% опасный подход. Сортировка битового массива и сортировка терабайтного массива данных генома потребуют сильно разных подходов и компромиссов. Сначала нужно думать про данные и типы, потом про код.


    Плюс накладывается мантра "повторять код нельзя" (DRY), которые многие новички возводят в абсолют, не понимая, что повторять код можно и нужно, пока он не повторяется, как минимум, 3 раза :) И дженерики тут кажутся какой-то магической пилюлей, которой и пользуются налево и направо, когда она есть, и утверждаются в мысли, что это необходимый компонент языков программирования.

  • Мысленный эксперимент: Flutter на Go
    –2
    Однако, при наличии дженериков факт нахождения виджета внутри стейта был бы лишь деталью реализации StateCore, всё что требуется от разработчика — знать, что методы стейта имеют доступ как к внешним свойствам, так и к внутреннему стейту. Ментальная модель не страдает.

    То есть есть стейт виджета, который embedd-ит некий StateCore, который магией дженериков параметризирован под наш виджет, а сам виджет находится внутри стейта? Мне даже представлять это больно, и моя ментальная модель (виджет  -> стейт) страдает. Вам реально нравится такой дизайн?


    Мне кажется это хороший пример того, за что не любят дженерики – не считая валидных кейсов вроде популярных структур данных и алгоритмов – на них начинают создавать вот такие монстроидальные дизайны (потому что можно!), которые сложно понимать, не говоря уже о рефакторинге и поддержке.

  • Мысленный эксперимент: Flutter на Go
    0
    Спасибо, отличный комментарий и пример. В принципе, там где виджеты без стейта, то API «пользовательских виджетов» остаётся таким же, а со стейтом — да, надо либо оставлять подход, как у меня описан (в build виджет не создаётся), либо придумывать что-то иное.

    Что мне не нравится в походе Flutter (и в вашем примере, соответственно) – это то, что этот вариант как-бы «работает», но он абсолютно не ложится на ментальную модель проблемной области. Для меня, например, это у виджета есть стейт, а не «стейт содержит виджет». В этом нет смысла, если читать этот код с нуля, пытаясь его замаппить на то, как мы понимаем и видим мир.

    Казалось бы – ну и что, в чём проблема? Но из моей практики, чем точнее код отражает то, как мы думаем о проблемной области, тем он дальше будет легче в понимании, рефакторинге и поддержке. Когда из «реального мира» появляется новое требование, оно основано на взаимосвязях вещей в реальном мире, и может сильно плохо ложится на тот код, который мы придумали, вопреки логичности маппинга.

    То что в вашем примере дженерики сделают на «одно приведение типа меньше» это да, только это не сильно решает проблему. Но спасибо за интересный пример, есть над чем поразмыслить.
  • Мысленный эксперимент: Flutter на Go
    0
    вполне себе существуют и даже видны на экране, и вполне имеет смысл наследование.

    Если честно, не совсем уловил суть.


    Go отлично позволяет передать взаимосвязь между объектами и виджетами, и я не вижу, что именно наследование тут может улучшить. Может покажете на примере кода?


    как отсутствие дженериков и «нормального» наследования в Go — для UI обычно это весьма полезно

    Опять же, не сочтите за троллинг, но можно ли на примере кода показать, как именно дженерики тут улучшат код?


    Наверное, было бы удобно, если бы сетевую часть можно было бы написать на Go

    Вот я сейчас исследую насколько gomobile легко подключить к Flutter приложению. Есть и помимо сети масса кейсов :)

  • Мысленный эксперимент: Flutter на Go
    –1
    Можно пруфы?

    В цитате, на которую вы ссылаетесь, есть ссылка "на пруфы".


    Это как-то очень печально, если вас запутывает

    Ничуть.


    но в обоих языках есть довольно запутанные моменты.

    Ок, спасибо.

  • Мысленный эксперимент: Flutter на Go
    +1
    Go, напротив, реализует подход communicating sequential processes (CSP).

    CSP и реактивное программирование не взаимоисключающи.


    Более того, это частая ошибка новичков в Go, узнав про каналы и горутины начинать их использовать везде – даже там где достаточно простого вызова функции. Так что наличие CSP в языке, не означает, что необходимо везде это использовать.

  • Мысленный эксперимент: Flutter на Go
    –1
    Да, я Desktop Embedder и эту ссылку упомянул в статье :)
  • Безликий код убьет программирование, и ничего мы с этим не сделаем
    +1
    Тони Хоар ещё в 1975 писал, что борьба с любителями избыточной сложности это будет долгий и нелегкий процесс, но вот такие посты прям показывают, что через 50 лет это не борьба, а война просто :)
  • За что я не люблю Go
    0
    В 2009 горутины были прорывом (наверное. Я в C# пользовался тасками примерно тогда же). Сейчас же это мейнстрим в большинстве языков.

    Горутины сами по себе не были новыми и не они есть причиной успеха Go для concurrent-кода. Новинкой была имплементация CSP-подобной логики как часть языка, и этого, насколько мне известно, пока в других языках нет.
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Спасибо, вот что-то подобное Wi-Fi Direct я очень давно хочу, чтобы было из коробки и стандартно для всех девайсов, но пока это далеко от истины. iOS-устройства, например Wi-Fi Direct не поддерживают, у них там своя технология.
  • В ядре Linux слово fuck заменили на hug
    0

    Именно, третьего порядка.

  • В ядре Linux слово fuck заменили на hug
    0

    Точно, респект. )

  • В ядре Linux слово fuck заменили на hug
    0
    Комментарии в этом треде – хорошая перекличка SSJW (борцов с борцами за справедливость). :)
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Спасибо. Да, чуть выше обсуждали уже альтернативные коды. У QR кодов всё-таки главный плюс это встроенная поддержка в мобильных SDK и вообще везде. Я могу такой анимированный QR сгенерировать даже через Apple Shortcut скриптинг :) Less is more.
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Спасибо. Да, безусловно, это пока эксперимент, и для более-менее серьезных продакшн проектов ещё сыроват. Там есть свои проблемы (связанные, опять таки, с фактом того, что в итоге всё равно JS на выходе) – например при частом обновлении виджета из горутины (прогрессбар быстрый, допустим), может понадобится вручную вызывать `runtime.Gosched()` иначе виджет не будет обновляться.
    Так что на данном моменте как полноценную замену существующих стеков для фронтенда не могу рекомендовать.
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Я не употреблял слово «кроссплатформенность».
    Была конкретная задача использовать код на трёх платформах и ни один из вышеприведённых языков не позволял сделать это достаточно просто.
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    С горячим соглашусь )

    Инструмент инструменту рознь. Мнение о том, что все инструменты одинаково хорошо, «просто нужно их получше узнать» мне чуждо, и, честно говоря, считаю его крайне вредным. Все программисты должны понимать, что выбор инструментов очень сильно будет влиять на их работу, продуктивность и даже дальнейшую судьбу. Но умение отличать плохие инструменты от хороших приходит с опытом, конечно.

    Про постоянную величину сложности фреймворков – это вот сильно мимо. Интересно было бы послушать, как вы пришли к такому выводу.
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Точно, спасибо!
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0

    Нет, не шутка.


    Я действительно не знаю способов использовать код на Java, Python или JS на ноутбуке, в вебе и в iOS и Android проекте. Возможно, нужно было добавить, что "непортируемым, в сравнении с Go", потому что в Go это всё однострочные команды и меньше секунды ожидания:


    Windows PE
    GOOS=windows go build


    Linux ELF
    GOOS=linux go build


    MacOS X Mach64
    GOOS=darwin go build


    iOS Framework
    gomobile bind -target=ios .


    Android AAR Native Code
    gomobile bind -target=android .


    Web (JS)
    gopherjs build


    И это всё фактически из коробки (gomobile и gopherjs ставятся отдельно также однострочниками).

  • Передача данных через анимированные QR на Gomobile и GopherJS
    +1
    Ну, настолько я усложнять не хочу — почти любое решение тут сильно ограничивает круг применения. Я вот только сегодня понял, что такую анимированную гифку даже Apple-овские Shortcuts скриптинг может сделать – можно сказать «Сири, сгенерируй-ка мне QR из последнего скриншота», и скрипт возьмёт скриншот, разобъет на куски (вот тут еще не уверен, умеет ли такое), сгенерирует QR на каждый и склеит в GIF-ку (такое точно умеет). В общем, less is more.

    Да и дорого слишком реализовывать протокол с подтверждениями – Шэннон не одобряет :) Я буду чуть позже фонтанные коды реализовывать, чтобы проблему с ожиданием цикла решить.
  • Передача данных через анимированные QR на Gomobile и GopherJS
    +1
    Асинхронный JS тоже только одно ядро умеет )
  • Передача данных через анимированные QR на Gomobile и GopherJS
    +1

    Здорово, спасибо за ссылку.
    Вот ещё очень подробный разбор, только обратного процесса и с интерактивом — https://www.nayuki.io/page/creating-a-qr-code-step-by-step

  • Передача данных через анимированные QR на Gomobile и GopherJS
    0

    Ага, есть вот даже такой проект: https://github.com/claudiodangelis/qr-filetransfer

  • Передача данных через анимированные QR на Gomobile и GopherJS
    0

    Узнать может и не сложно, но что делать, если генерация заняла больше времени, чем задержка между кадрами? (а там почти всегда так). А горутины всё равно в скомпилированному JS будут в одном потоке работать.


    принимающей стороны кадры должны быть синхронизованы с передающей?

    Как раз нет — рассинхронизация частот будет влиять только на общее время передачи, но всё равно будет работать. Я там в описании идеи протокола немного объяснял этот момент — фреймы можно вообще в любом порядке принимать.

  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Я не нашёл, но полагаю что маленькая – не под скорость, всё же, оптимизировали )
  • Передача данных через анимированные QR на Gomobile и GopherJS
    0

    Спасибо )

  • Передача данных через анимированные QR на Gomobile и GopherJS
    0

    В принципе можно, просто GIF удобней был тем, что можно гарантировать FPS – там задержка между кадрами прямо в файл записывается. При генерации каждого кадра, по идее, достаточная порция времени будет уходить на генерацию картинку, и высчитать, сколько реально осталось ждать до следующего кадра будет не сильно тривиально. Хотя стоит попробовать, конечно, всё равно.

  • Передача данных через анимированные QR на Gomobile и GopherJS
    0

    Там крутая технология, я пробежался по обоим патентам – если вкратце, то картинку они могут какую угодно рендерить (не обязательно этот шарик с частицами). Они анализируют усреднённое изменение в luminance (светимости), и передают данные именно через этот параметр, а вот chrominance (цветовая составляющая) там всё равно какая. Гениальность в том, что человеческий глаз гораздо более чувствителен к изменениям в цветовой составляющей, чем в светимости, и не видит тех изменений, но софт очень надёжно может из изменений светимости вытащить данные.

  • Передача данных через анимированные QR на Gomobile и GopherJS
    0
    Ну, я не особо хочу озвучивать свою позицию про JS, чтобы не провоцировать холивары, но язык программирования это слишком важный и сложный инструмент, чтобы лепить его за пару недель и хаотически развивать. Есть такая известная цитата:
    We become what we behold. We shape our tools and then our tools shape us

    Инструменты, которые мы используем для решения задач, формируют то, как мы их решаем, наши дальнейшие решения, принципы и ценности, и, в итоге, всю экосистему. То, во что превратил наспех созданный язык для скриптиков, индустрию разработки софта – это сильнейший удар по борьбе с добавленной сложностью, с которой боролись пионеры computer science, такие как Дейкстра и Хоар, ещё в 70-е.

    Поэтому да, моя главная претензия не столько к самому языку (что можно ждать от быстрой поделки), а к тому, что благодаря историческим недоразумениям это стало единственным языком доступным на внезапно ставшей основной платформе, и сформировало очень поломанную экосистему и целые поколения разработчиков, выращенных на поломанных принципах, сильно далеких от основного мейнстрима мира программирования. Эх, всё равно холиварно получилось.