• Автоматическое разрешение конфликтов с помощью операциональных преобразований
    0

    На схеме, где Васе приходит Insert " Habr" @5, там же ошибка в действиях, которые происходят на Васином клиенте

  • Пацаны, так Ruby умер или нет?
    0

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


    Фриз ничего такого не предоставляет.

  • Зависимые типы — будущее языков программирования
    0

    ну это совсм не идрис, я намешал разных синтаксисов разных языков в одном языке, это нигде не скомпилируется в таком виде.


    По моему нет, мне приходится вот так писать, но я в идрисе не шарю, тутор пролистал год назад, так что моим словам верить нельзя


    record Name where
      constructor MkName
      value : String
      p : LT 0 (length value)
    
    mkName : (value : String) -> {auto p : LT 0 (length value)} -> Name
    mkName value {p} = MkName value p
    
    name : Name
    name = mkName "kana"
  • Зависимые типы — будущее языков программирования
    0

    Есть такой способ "сэмулировать" зависимые типы как синглтон-типы. Синглтон типы — это типы с одним значением. В данном контексте мы имеем в виду, что если у "исходного" типа будет N значений, то синглтон-типов будет тоже N на каждое значение. Это нужно для того, чтобы на типах полностью сохранить (продублировать) значение.


    data Nat = Z | S Nat
    -- generate also "kind" Nat (there are no kinds anymore) and two types Z : Nat, S : Nat -> Nat, using DataKinds
    data SNat :: Nat -> Type where
      SZ :: SNat Z
      SS :: SNat n -> SNat (S n)
    
    SS (SS (SZ)) :: SNat (S (S Z))

    и теперь можно делать примитивные зависимости. Подобный подход реализован у flow в js. По крайней мере так это выглядит. Можно представить систему типов flow как:


    • у нас есть синглтон типы на все значения:
      (true : true);
      (false : false);
      (2 : 2);
      ("hello" : "hello");
      ([1, 2] : [1, 2]);
    • у нас есть юнионы типов (не суммы, а именно юнионы) и апкастинг к юниону от его элементов:
      type bool = true | false;
      ((true : true) : bool);
      ((2 : 2) : 2 | 3) : number);
      // где number = 0.5 | 1 | 2| 3 | ... встроенный
    • есть and-тип, требущий выполнения обеих сингнатур:
      (f : (true => string) & (false => number));
    • и есть даункастинг в виде тайп рефайнмента:

    declare function f(x: true): string;
    declare function f(x: false): number;
    function f(x: boolean): string | number {
      if (x === true) {
        (x: true);
        return 'hello';
      } else {
        (x: false);
        return 42;
      }
    }
    
    const x: string = f(true);
    const y: number = f(false);
    // x: number = f(true) будет ошибка типов
  • Зависимые типы — будущее языков программирования
    0

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


    Математически мы имеем тип


    Name = { s : String | 0 < length s }

    выражаем его "логически"


    Name = exist (s : String). 0 < length s

    переводим в типы:


    Name = (s : String, p : 0 < length s)

    часто это описывают так еще


    isName s = 0 < length s
    Name = (s : String) * isName s

    еще любят такой поинтфри


    Sigma (A : Type) (B : A -> Type) = (x : A, B x)
    Name = Sigma String isName

    еще структуркой можно написать


    record Name where
      constructor MkName
      value : String
      proof : 0 < length value
    
    f : Name -> String
    f { value } = "Hello " <> value <> "!!!"
    
    print $ f (MkName "kana" _) -- можем вывести пруф автоматически иногда, зависит от компилятора
  • Зависимые типы — будущее языков программирования
    0

    Или скорее всего нет, опять же, из-за человеческой лени. Работать с вездесущим Maybe неудобно, работать вообще с неправильно спроектированной моделью неудобно.

  • Четыре колеса — хорошо, два — лучше
    0

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

  • Четыре колеса — хорошо, два — лучше
    0

    Да лекго все объезжаются даже без особого сбрасывания скорости, это не устройство для тарана и довольно маневренное

  • Четыре колеса — хорошо, два — лучше
    0

    https://www.youtube.com/watch?v=S8Q4pl4BYjU


    У топовых самокатов скорость до сотни кмч

  • Четыре колеса — хорошо, два — лучше
    +1

    Когда как. Купил Met Parachute и надеваю, когда собираюсь выехать за город или покататься по лыжным трассам на скоростях выше 40, так-то его нужно бы одевать всегда после скорости в 20кмч, но лень. Но в последнее время заставляю себя чаще.

  • Четыре колеса — хорошо, два — лучше
    0

    Погода всегда известна заранее, на всякий случай я всегда вожу с собой легкую складываемую в карман курточку, когда холодно или дождь. Обувь заливало, благо почти всегда езжу в октрытой обуви или с дырками/вентиляцией в такую жару.


    Думаю да, во многом ситуация такая же как у велика или другого открытого транспорта. Не понятно, зачем вы спрашиваете, если это очевидно.


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

  • Четыре колеса — хорошо, два — лучше
    0

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


    Да, очевидно комфорта будет меньше от езды на открытом транспорте в непогоду, чем в закрытом, не думаю, что это можно и нужно оспаривать как-то.

  • Четыре колеса — хорошо, два — лучше
    +2

    У вас очень устаревшие данные, самые медленные современные самокаты едут 25+, а колеса и подавно быстрее.


    Мое колесо развивает скорость до 50, ездил на 43 максимум сам, в среднем еду 35, на прямых дорогах 40, как выше уже сказал. По прямой автобус обогнать тяжело, но он останавливается на каждой остановке и уже через 5 остановок будет отставать на 2. Такси нужно ждать столько времени, сколько хватило бы проехать больше половины пути на колесе.


    Прямая трасса не нужна, колесо 18 дюймов, без проблем заезжает на бордюры, скорость конечно нужно сбавлять хотя бы до 20, дороги в моем городе ужасны и это крайне чувствовалось на самокате с колесами в 8 дюймов, но на колесе не чувствуется вообще. Повороты же вообще никак не мешают езде, снизил скорость до тех 20 на секуду, повернул, снова за секунду разогнался до 40.


    Я замерял, до работы я заезжаю в лучшем случае за 18 минут, в среднем за 20. И при этом не нужно ждать транспорт. Если я проснусь за 25 минут до того, как мне срочно нужно будет быть на работе, на такси я точно не успею, в отличии от колеса.


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

  • Четыре колеса — хорошо, два — лучше
    +5
    Ну серьезно никто же не будет на них ездить на работу

    Почему? Именно так и делаю, мне до работы всего 7км, сначала добирался на сяокате, теперь на моноколесе. И половина коллег на велосипеде. После целой жизни езды на велосипеде смотрю на колесо как на нереальный шаг развития, потому что:
    а) очевидно быстрее, чем на велосипеде (да что там говорить, быстрее чем на автобусе и даже такси, который ждать минимум минут 10, когда я до работы за 15-20 добираюсь на колесе), средняя моя скорость — 35км/ч;
    б) не потею и не устаю, к сожалению нет душа в офисе и это критично.


    А как круто ехать по городу и пить чай со своей кружки.


    Желаю вам просто сначала попробовать.

  • Четыре типажа программистов
    +2

    Рокстары только и делают, что ставят себе задачи на "слабо". И когда им говорят, что они что-то не смогут, они только рады и делают все, чтобы показать, что они намного круче, чем о них думают, изучат материал и таки сделают. И они рады, и заказчик.


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

  • Четыре типажа программистов
    +1

    Должен сказать, что рокстару абсолютно не интересно делать свой гугл, его в принципе все эти компании мало заботят. Платят, дают свободу, есть с кем поделиться идеями — вот и хорошо.

  • Два года с Dart: о том, как мы пишем на языке, который ежегодно «хоронят» (часть 1)
    +1

    На всякий случай добавлю, что у webpack тоже есть tree shaking с ES6 модулями (если используется babel, нужно в конфиге отключить трансформацию импортов/экспортов), да и google closure compiller тоже вроде как умел в такое.

  • Дзен не позвонит
    0

    Интересно, что на моей памяти люди именно с vuex на redux и сбегают. Недавно один знакомый заменил vuex на redux + revue, сказал, что всё стало заметно проще и лучше.

  • Дзен не позвонит
    0

    Поле вычисляемое, может лучше сделать его через селектор и не хранить в сторе? (Стор должен быть минимальным и хранить минимум инфы, всю выводимую/вычисляемою инфу нужно выносить в селекторы).


    А селектор так и будет выглядеть:


    const isAppLoading = createSelector(
      [postForms],
      any(prop('isLoading'))
    );
  • Дзен не позвонит
    0

    Прошу прощения, но с таким же функционалом на каком-нибудь jquery я могу написать прямо в одном файле в index.html в script-тэге, это совсем не показатель.

  • Почему не работает Tree Shaking и как с этим жить
    +1

    Есть плагин для babel, который преобразует


    import _ from 'lodash';
    import { add } from 'lodash/fp';
    
    const addOne = add(1);
    _.map([1, 2, 3], addOne);

    в


    import _add from 'lodash/fp/add';
    import _map from 'lodash/map';
    
    const addOne = _add(1);
    _map([1, 2, 3], addOne);

    Вроде как можно настроить не только для lodash, если аналогичные плагины для ramda (бандл заметно уменьшается).


    Когда тришейкинг начнет работать, можно будет просто убрать этот плагин, не меняя код.


    Ссылка — https://github.com/lodash/babel-plugin-lodash

  • Вы знаете, что такое трансдьюсеры
    0

    Я процитирую себя же:


    Трансдьюсеры — апгрейд для функциональных трансформаций

    Циклы были заменены на трансформации, как более чистое, иммутабельное, функциональное, декларативное и т.д решение, но из-за этого пришлось пожертвовать производительностью. Трансформации были заменены на останавливаемую свертку (reduce + трансдюсеры для создания сложной логики), что сильно увеличило производительность.


    В гисте ниже я выполнил пару оптимизаций:


    • трансдьюсеры/редьюсеры хороши тем, что мы можем работать с транфсормацией как с объъектом, это их сильная сторона (реюзабельность), поэтому я вынес их (по сути как aot-компиляция, которая проводится для циклов).
    • как тут советовали где-то выше в комментах, нет смысла делать concat, потому что стейт нигде больше не используется, адекватно будет заменить на push. Как оказалось, существенной роли это не сыграло, concat видимо как-то оптимизируется.

    Name: Array methods
    HZ: 4642.041341511592
    Count: 240
    Name: Transducer
    HZ: 161841.1974395176
    Count: 8669
    Name: For
    HZ: 786935.9631453991
    Count: 40472

    Функциональные трансформации медленнее циклов на 3 порядка, но трандьюсеры быстрее их на два.


    https://gist.github.com/kana-sama/d0d5eb8362dda32841cc58302fa1c191

  • Вы знаете, что такое трансдьюсеры
    0

    Нет, циклы заведомо быстрее и нет смысла с ними даже сравнивать)

  • Вы знаете, что такое трансдьюсеры
  • Вы знаете, что такое трансдьюсеры
    +1

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


    То есть разница между [1...100000].filter(..).map(..).take(5) и аналогичным решением на трансдьюсерах будет различаться на N порядков.

  • Вы знаете, что такое трансдьюсеры
    +2

    Да, скажем, ClojureScript вполне спокойно реализовал всякие персистентные иммутабельные типы типа векторов, списков, хэш-мапов, множеств на js. Все это прогоняется через google closure compiler и реакт-приложения с иммутабельным стейтом на кложовских типах данных работает быстрее, чем react + redux на pure js-объектах.

  • Вы знаете, что такое трансдьюсеры
    +1

    Правильный выход — использовать персистентные структуры данных.

  • Вы знаете, что такое трансдьюсеры
    +1

    На входе всегда один редьсер. А вот с помощью операции композиции функций (не трандьюсеров) мы можем скомбинировать несколько, но мы можем сделать это и без нее по цеопчке t3(t2(t1(r)))

  • Вы знаете, что такое трансдьюсеры
    +3

    К счастью, на языках, на которых я пишу, циклов в принципе нет (Clojure, для развлечения Elm и Haskell)

  • Вы знаете, что такое трансдьюсеры
    +3

    а) писать всё это не придется, все это уже реализовано в библиотеках для работы с данными (ramda, clojure.core)
    б) использовать императивные циклы я в принципе не считаю хорошей идей, а трансдьюсеры будут намного быстрее, чем .map/.fiter на массивах с большим количеством данных. Ленивые коллекции (как в хаскеле, кложе, lo-dash) или трандьюсеры тут лучший вариант.


    Но естественно использовать это для мелких задач поиска по массиву в 10 элементов — оверхед.

  • Вы знаете, что такое трансдьюсеры
    0

    Не думаю, ведь вынести куда-то yield нельзя, разве нет? Лучше просто использовать какую-нибудь стримовую библиотеку типа xstream

  • Вы знаете, что такое трансдьюсеры
    +1

    take(0) вернет начальное состояние, потому что не пройдет первая проверка if (n > 0) и take вернет аккумулятор (а это и есть начальное состояние, если take до этого не выполнялся) вместо отправки аргументов по цепочке дальше.


    Другое дело, что все предыдущие редьюсеры выполнятся впустую.

  • Вы знаете, что такое трансдьюсеры
    +2

    Рад, что статья хоть как-то оказалась вам полезна )

  • Вы знаете, что такое трансдьюсеры
    +1
    Работать со значением в самом трансдьюсере тоже становится не просто:

    Да, это действительно проблема. Но проблема скорее в моей реализации функции reduce, можно попробовать придумать другой способ завершать её выполнение, но у меня идей нет. Можно сделать Reduced каким-нибудь прокси.

  • Вы знаете, что такое трансдьюсеры
    +2
    Как реализовать пустое перечисление, которое не вернет ни одно элемента?

    Нужно просто возвращать acc. reduce требует начальное состояние, именно ого и вернётся для пустого перечисления. При отстуствии начального состояния он берет первый элемент и при пустом массиве Array#reduce кидает исключение.


    Должен заметить, что в посте я привел простую реализацию, когда как реально все немного сложнее. Трансдьюсер — это кортеж из трех функций с разным числом аргументов (имена фунцкий образные):


    • init — не принимает аргументов и должна вернуть начальное состояние. Зачастую просто вызывает next, то есть делегирует init на следуюущий трансдьюсер.
    • step — это то, что в статье — два аргумента: аккумулятор и элемент, возвращает аккумулятор
    • result — принимает один аргумент — аккумулятор, срабатывает после завершения reduce.

    Кажется такая реализация take не композится сама с собой. Если вызвать take(1)(take(1)), то результат будет обернут в Reduced дважды.

    Да, это действительно так, спасибо за замечание. Кложурная версия вместо reduced использует ensure-reduced, которая работает как identity, если значение уже обернуто. Исправлю.

  • Вы знаете, что такое трансдьюсеры
    +2
    вас же не просят их имплементировать

    они в понимании проще, чем ленивые коллекции, ведь это просто вызов функций по цепочке. Или вы к тому, что не обязательно понимать то, что используешь?


    ни чем не обоснованое утверждение

    да, это субъективное мнение после чтения исходного кода обеих библиотек


    это круто, но зачем? единственное здравое объяснение, которое приходит в голову – тестирование. Но это не проблема

    уменьшение повторения кода?


    Про последний пункт насчет того, зачем это нужно: трансдьсеры частично решают ту же проблему, что и ленивые коллекции в lodash. Но решение через трансдьюсеры проще хотя бы потому, что тут больше нет довольно сложных ленивых коллекций, нет лишний преобразований с _ и value, нет методов, только функции, и это самый обычный reduce. Вроде как обосновал, нет? (простой паттерн — ведь это просто последовательное выполнение функций, большая эффективность — не выполняются лишние трансформации).


    Ramda:


    const result = into([], compose(
      filter(({ my, others }) => my > others),
      pluck("gameID"),
      take(2),
    ), scores)

    Lodash:


      _(scores)
        .filter(({ my, others }) => my > others)
        .map("gameID")
        .take(2)
        .value()

    Да, обычный цикл конечно проще, но обычный цикл еще и мутабелен, и императивен, ну а (субъективно) читать императивный код сложнее, чем декларативный.


    Я мало что знаю про highload, но в xstream свертка вместо возвращения значение при завершении стрим возвращает новый, состоящий из результатов сверток после каждого элемента, то есть


    ------1-----1--2----1----1------
      fold((acc, x) => acc + x, 3)
    3-----4-----5--7----8----9------

    const map = fn => next => (acc, x) => next(acc, fn(x))
    const filter = pred => next => (acc, x) => pred(x) ? next(acc, x) : acc
    const compose = (f, g) => x => f(g(x))
    const append = (arr, x) => arr.concat([x])
    const isOdd = x => x % 2 === 1
    const inc = x => x + 1
    const double = x => x * 2
    
    const xs = xstream.default
    
    const button = document.querySelector("button")
    
    const clicks$ = xs.create({
      start(listener) {
        button.addEventListener("click", () => listener.next())
      },
      stop() {
        button.removeEventListener("click", next)
      }
    }).fold(inc, 0)
    
    const doubledOdds$ = clicks$.fold(compose(
      filter(isOdd),
      map(double),
    )(append), [])
    
    doubledOdds$.addListener({
      next(x) { console.log(x) }
    }) // [2, 6, 10, ...]

    Исключетельно чертой трансдьюсеров является то, что они ничего не знают о контексте. Они не знают, как добавлять элемент в конец массива, поэтому вместо этого они делегируют это на оборачиваемый ими редьюсер.


    Пока писал это, понял, что я недостаточно компетентен в этой области. Все, что могу сделать — дать ссылку на статью про трансдьюсеры от Рича Хикки — их создателя — https://clojure.org/reference/transducers. Там же есть ссылка на пост в блоге и видео.

  • Круглый график на Canvas
  • Вы знаете, что такое трансдьюсеры
    +1

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


    x |> f = f x
    
    main =
        [1..]
            |> filter even
            |> map (* 2)
            |> take 3
            |> print -- [4, 8, 12]

    Несколько недель назад я такое тоже писал:
    image

  • Вы знаете, что такое трансдьюсеры
    +3

    Увеличение уровня абстракции в данном случае помогает использовать трансдьюсеры со всеми сворачиваемыми типами (массивы, стримы в rxjs, множества, словари) независимо от того, реализовали ли они функции map/filter/take. Чисто теоретически, если бы трансдьюсеры бы встроили в js, то из всех библиотек типа rxjs/xstream все эти трансформации можно было бы выпилить, оставив только reduce, потому что универсальные трансдьюсеры будут работать однообразно везде.

  • Вы знаете, что такое трансдьюсеры
    +1

    Да, можно, в js тоже есть библиотеки с ленивыми коллекциями, а в clojure все трансформации по умолчанию ленивы (но при этом ленивые трансформации проигрывают по скорости трансдьюсерам.


    Но работа с трансформациями как с объектом первого класса — фишка трансдьюсеров, которая позволяет
    а) переиспользовать их
    б) использовать одни и те же трансдьсеры для любых типов данных, которые поддерживают свертки (Foldable), скажем, стримы в rxjs (те же самые трансдьюсеры будут прекрасно работать и там), а значит из этих библиотек можно было бы спокойно выпилить все трансформации, если бы трансдьюсеры были бы нативными и общеиспользуемыми (маловероятное будущее, но в clojure стремятся к этому)


    image