Pull to refresh

Comments 67

Логичнее бы & смотрелся. Ведь именно этот символ участвует в нынешней реализации

?.. — это элвис оператор. Его скорее всего подтянули из других языков.
Странно описан абзац про производительность. Так как фактически это синтаксический сахар.
Код


left?.right

будет эквивалентен


left && left.right

Ну, тут фокус в том, что прежде чем написать left && left.right программист подумает а может ли left отсутствовать в принципе — а всякие left?.right можно привыкнуть писать просто по умолчанию.

UFO just landed and posted this here
Зачем вы так говорите? Сейчас вас придут минусовать все хаскельщики. Все полтора.
UFO just landed and posted this here
В Haskell для этого есть &, один символ (перевёрнутая версия $). Но в Haskell любой оператор — это обычная инфиксная функция, так что если мне что-то уродливо, я могу сделать так:
a ⊥ b = b a
и всё, у меня теперь есть этот оператор, который вы ещё джва года будете ждать.

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

А вообще в этот ваш жовоскрипт постепенно переезжает весь LiveScript в плане сахара.
UFO just landed and posted this here

юзал в Elixir ( https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2 ), крайне удобный operator для pipes (проброса выхлопа одной функции на вход другой).
этот паттерн — древний и юзается например в linux для перенаправления output одной прграммы на input другой (https://ru.wikipedia.org/wiki/%D0%A4%D0%B8%D0%BB%D0%BE%D1%81%D0%BE%D1%84%D0%B8%D1%8F_Unix


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

http://www.linfo.org/pipes.html
)
здесь — применяют этот паттерн для конекта функций


vlreshet


О себе
Пишу на JS, PHP, Java и бумажных листиках.

теперь стало понятно)

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

Подскажите людям, далёким от настоящих функциональных языков, что это такое и как там переиспользуется результат?

Композиция функций в функциональных языках — это бинарный оператор на множестве функций. Т.е. он также порождает функцию.
Пусть f и g – функции, например:
f :: Int -> Int
f x = x + 1

g :: Int -> Int
g x = x * 2

Тогда оператор композиции функций (.) порождает значение на множестве функцию, т.е. новую функцию:
g . f -- Точка — бинарный оператор, порождающий новую функцию

Это значит, мы можем объявить новую функцию, являющуюся композицией функций:
h x = g . f $ x

В итоге имеем функцию h:
h :: Int -> Int -- Объявление типа здесь для ясности
h x = g . f $ x

Вызов которой равноценен вызову g(f(x)).
Вот простой пример:
f :: Int -> Int
f x = x + 1
g :: Int -> Int
g x = x * 2

h x = g . f $ x

main = do 
  print (h 10) -- Печатает 22

Представьте следующий пример на TypeScript (для ясности, поскольку он типизирован):
const f = (x: number): number => x + 1;
const g = (x: number): number => x * 2;

Как бы мы могли реализовать функцию compose, чтобы она была похожа на оператор композиции функций?
// Опрередим тип для функции, принимающей параметр типа P и возвращающая результат типа R
type F<P, R> = (value: P) => R;

const compose = <P, R, O>(f1: F<R, O>, f2: F<P, R>) => (x: P): O => f1(f2(x));

// Мы можем применить эту функцию создания новой функции:
const h = compose(g, f); 

h(10); // Возвращает 22

// Теперь представьте, что в языке есть оператор (.) для записи compose:
const h = g . f;

// Поскольку этот оператор определён на множестве функций и он правоассоциативен, допустимо использовать несколько функций:
const m = h . g . f;
// Это эквивалентно следующему коду:
const m = h . (g . f);
// Или следующему, если переписать с использованием функции compose:
const m = compose(h, compose(g, f));

Соответственно, отличие пайп-оператора в том, что он не возвращает новую функцию, а просто применяет функцию к параметру.
const pipe = <P, R>(x: P, f: F<P, R>): R => f(x);

pipe(10, f); // Возвращает 11

// Теперь представьте, что в языке есть оператор (|>) для записи pipe:
const result = 10 |> f;
// Поскольку этот оператор левоассоциативен, допустимо использовать его с несколькими значениями:
const result = 10 |> f |> g;
// Это эквивалентно следующему коду:
const result = (10 |> f) |> g;
// Или следующему, если переписать с использованием функции pipe:
const result = pipe(pipe(10, f), g); // Возвращает 22

Разница, думаю, очевидна :)

Спасибо, теперь понятно. Лично мне в моём |> гораздо полезнее, чем .. По сути я вижу в нём удобную возможность писать код слева-направо без ада скобочек. Ну и возможность писать цепочки преобразований даже тогда, когда нужного метода нет в прототипе объекта.


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


А почему он, кстати, справа-налево?


P.S. мне кажется f . g . h в Haskell (или что это за язык?) на деле гораздо дешевле/быстрее, чем compose(f, g, h) в JS.

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

Некоторые люди ждут pipe-оператора гораздо гораздо больше ?., ?? и любых других ??????. Это же возможность писать функциональный код слева на право! А изнутри-налево, как сейчас. И это избавления от ада скобочек. И это возможность продолжить цепочки методов методами, которые в цепочке отсутствуют. Да это моя самая желанная вещь со времён async-await! :)

Поддерживаю полностью, первые два — приятный синтаксический сахар, а пайплайн — это прямо новая парадигма, по сути, API библиотек будут создаваться с учетом него, это круто.
К сожалению, комбинация операторов — и > уже существует в языке…
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
А что если у объекта уже есть метод do?
UFO just landed and posted this here
Вот только применять такую конструкцию можно будет только для своих объектов…

Для null и undefined не получится это реализовать.


Зато вариант с .do можно реализовать прям сейчас в 5 строчек кода, без дописывания компилятора.

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


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

Как угодно, но не так. Вы взяли уже существующий оператор . и расширили его странным поведением в совершенно базовых условиях, ломая весь существующий js-код. Как вам такое в голову пришло? :) К тому же вы явно невнимательно посмотрели все use-case pipe-оператора.

Есть уже существующие библиотеки, например Рамда:


const R = require('ramda')

let resultA = R.pipe(
  doubleSay,
  capitalize,
  exclaim
)('hello')

let resultB = R.compose(exclaim, capitalize, doubleSay)('hello')

Разница между pipe и compose только в порядке применения функций?

Да.


В JS операторы идут в порядке приоритетов, методы чейнятся от объекта слева направо, а функции оборачивают аргументы справа налево, образуя кучу вложенных скобок.


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


По этому в библиотеке собрано много функций обёрток над операторами яваскрипта. А чтобы не получилось много вложенных скобок был сделан конвейер compose.


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

Предлагаю оператор o_O. Смотрите, как красиво:
let result = "hello" o_O doubleSay o_O capitalize o_O exclaim;
А вместо?.. и ?? использовать :/ и х_Х соответственно.

var result = capitalize(doubleSay(obj && obj.value? value: «hello»))
V
const result = obj:/value x_X «hello» o_O doubleSay o_O capitalize
почему у меня ощущение что на каждом шаге он удивляется всё больше и больше?
В принципе, это ведь синтаксический сахар.
Почему бы и нет? Похожие нововведения местами улучшили синтаксис PHP, и для JS это не особо повлияет на концептуальность языка.
В PHP ?: и ?? чистый сахар, однозначное и простое приведение к известным «паттернам». Тут с?.. сложнее, по-моему.
Вместо a.b?.c?.d?.e гораздо читабельнее было бы ?(a.b.c.d.e) или даже nullsafe(a.b.c.d.e)
Так нельзя выразить, на каких местах точно не null, чтобы компилятор не генерировал лишнюю проверку.

Вот эти выражения по смыслу разные, как их отличать: a?.b?.c.d.e, или a.b?.c.d.e, или a?.b.c.d?.e
Лишняя проверка ничего не стоит практически, и в большинстве случаев неизвестно точно какие поля не null, так что будет повсеместно нечитаемая лапша a.b?.c?.d?.e
Вариант ?(a.b.c.d.e) мне нравится и я бы хотел его видеть как дополнение к введенному. Можно было бы тогда так делать записи — a?.b.?(c.d.e). Но «цепочная» проверка будет постоянно производиться в таких выражениях a?.b.?(c.d.e) и a?.b.?(c.d.f), что не очень хорошо может сказаться на производительности, если таких «хвостов» много. Для меня легче всегда делать разложение проверки на уровни.
UFO just landed and posted this here
UFO just landed and posted this here
Не знаю, на мой вкус?.. порождает странный код, в котором мы сразу ломимся к непроверенной сущности, вместо того, чтобы где-то раньше проверить, что сущности нет, и уже не пытаться копаться в её потрохах. Может, нужна какая-то простая конструкция, проверяющая, что нужные поля у структуры/объекта есть (а заодно, например, что они проходят какие-то ещё проверки — по регекспам, например)?

Ну, что-то типа

  match(user, 
     { login : /[A-Za-z]{1,10}/, 
        street : { 
            id : /[0-9]+/, 
            name: /[A-Za-z 0-9]+/ }, 
            password : function(v) { return true; }  
                    }
     } ); 

Опциональные свойства могут быть даже у проверенных сущностей.

Хех. Когда, по прогнозам, JS догонит Perl? В том смысле, что любая комбинация знаков препинания будет рабочим кодом? :)
На самом деле тут вопрос в удобстве. А то, что кому-то не нравится просто прихоти. К любому синтаксису привыкаешь и через некоторое время посмотрев на предыдущие работы с недоумением смотришь какой я некрасивый код писал. Очень полезные нововведения как по мне.
FiraCode как-нибудь покрасивее оформит комбинацию |> и будет счастье

Кстати да. Сейчас там такое есть (в JS не работает):
image

Рискую получить тапком в лицо…
Почему FiraCode выглядит нереально убого? Это «что-то» сверх-расфокусированное, от чего глаза почти сразу кровью течь хотят. Или у людей это «четкий» шрифт?
Вполне все «четко» выглядит, даже на Ретине.

Это не от FiraCode зависит, насколько я понимаю, а от вашего экрана. Я привёл скриншот, и если он бьёт вам по глазам, то рискую предположить, что установи вы его себе на компьютер, у вас он будет выглядеть иначе. Т.к. это зависит от ОС и её настроек. А ещё хочу отметить, что шрифты на картинках выглядят часто непривычно или даже не красиво, а будь вы рядом с конкретным монитором, где эта картинка была сделана, вам бы оно могло показаться очень даже ничего. Всё дело в DPI. У меня 3 монитора (мне так удобнее) с разными DPI и на всех 3 всё выглядит немного иначе. Бывает люди подгоняют себе такой DPI и монитор, что им комфортно использовать 1px шрифты (когда все линии равны аккуратно 1px), и в других условиях оно выглядит просто ужасно, а у них всё симпатично.


В общем шрифты это дико холиварная тема. Скажем у меня здесь на хабре стоит 130% zoom и все картинки выглядят мылом. Но зато всё большое и читать приятнее. Компромисс :)

Я привёл скриншот, и если он бьёт вам по глазам

o_O… пардон, из-за диагональных текстов думал, что это «просто картинка». Нет, на ней как раз все выглядит отменно. Плохо же выглядит в IDE, где я естественно, первым делом, и побежал смотреть. Увидев и разочаровавшись, пошел тестировать на машине коллеги. Там так же «не очень»/«сплошное мыло».
Всё дело в DPI.

Ок, это многое объясняет, не подумал сперва — виноват.
Конструкции хороши, мне понравились.
Всяких коротких языковых конструкций можно еще в perl`e подсмотреть.
UFO just landed and posted this here
Я никогда не понимал, почему нельзя было сделать обычно, что обращение к undefined возвращает undefined
Потому что, если Вы вдруг обращаетесь к свойствам undefined, то, вероятно, это ошибка. Если тихо пропускать это, это может привести к багам.

Ну и раз появится оператор «?.», то уже нет разницы.
Если тихо пропускать это, это может привести к багам.

Просто в конце длинной цепочки вычислений вы получите undefined. Искать ошибку будет труднее.

UFO just landed and posted this here
UFO just landed and posted this here
undefined() Uncaught TypeError: undefined is not a function
undefined.test Uncaught TypeError: Cannot read property 'a' of undefined

Или вы не понимаете почему оба так работают?
Sign up to leave a comment.