Как стать автором
Обновить

Комментарии 67

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

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


left?.right

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


left && left.right

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

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

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

А вообще в этот ваш жовоскрипт постепенно переезжает весь LiveScript в плане сахара.
НЛО прилетело и опубликовало эту надпись здесь

юзал в 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 библиотек будут создаваться с учетом него, это круто.

--> красиво смотрелся бы для pipelines

К сожалению, комбинация операторов — и > уже существует в языке…
НЛО прилетело и опубликовало эту надпись здесь
a-->b это b(a) или a-- > b
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
А что если у объекта уже есть метод do?
НЛО прилетело и опубликовало эту надпись здесь
Вот только применять такую конструкцию можно будет только для своих объектов…

Для 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)

Или a.b.c.d.e ?? default

А if(a?.b) {} как?

if (a.b ?? false)

Так нельзя выразить, на каких местах точно не 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), что не очень хорошо может сказаться на производительности, если таких «хвостов» много. Для меня легче всегда делать разложение проверки на уровни.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Не знаю, на мой вкус?.. порождает странный код, в котором мы сразу ломимся к непроверенной сущности, вместо того, чтобы где-то раньше проверить, что сущности нет, и уже не пытаться копаться в её потрохах. Может, нужна какая-то простая конструкция, проверяющая, что нужные поля у структуры/объекта есть (а заодно, например, что они проходят какие-то ещё проверки — по регекспам, например)?

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

  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 подсмотреть.
НЛО прилетело и опубликовало эту надпись здесь
Я никогда не понимал, почему нельзя было сделать обычно, что обращение к undefined возвращает undefined
Потому что, если Вы вдруг обращаетесь к свойствам undefined, то, вероятно, это ошибка. Если тихо пропускать это, это может привести к багам.

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

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

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
undefined() Uncaught TypeError: undefined is not a function
undefined.test Uncaught TypeError: Cannot read property 'a' of undefined

Или вы не понимаете почему оба так работают?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий