Comments 67
?.. — это элвис оператор. Его скорее всего подтянули из других языков.
Странно описан абзац про производительность. Так как фактически это синтаксический сахар.
Код
left?.right
будет эквивалентен
left && left.right
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
)
здесь — применяют этот паттерн для конекта функций
О себе
Пишу на 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.
А почему он, кстати, справа-налево?
stackoverflow.com/questions/29878545/why-does-the-dot-compose-from-right-to-left-in-haskell
В предложении есть еще несколько примеров использования, может они вас убедят:
Некоторые люди ждут pipe-оператора гораздо гораздо больше ?.
, ??
и любых других ??????
. Это же возможность писать функциональный код слева на право! А изнутри-налево, как сейчас. И это избавления от ада скобочек. И это возможность продолжить цепочки методов методами, которые в цепочке отсутствуют. Да это моя самая желанная вещь со времён async-await! :)
-->
красиво смотрелся бы для pipelines
a-->b
это b(a)
или a-- > b
Для 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
, который немного не соответствует общей филосософии рамды.
let result = "hello" o_O doubleSay o_O capitalize o_O exclaim;
Почему бы и нет? Похожие нововведения местами улучшили синтаксис PHP, и для JS это не особо повлияет на концептуальность языка.
Или a.b.c.d.e ?? default
Вот эти выражения по смыслу разные, как их отличать: a?.b?.c.d.e, или a.b?.c.d.e, или a?.b.c.d?.e
Ну, что-то типа
match(user,
{ login : /[A-Za-z]{1,10}/,
street : {
id : /[0-9]+/,
name: /[A-Za-z 0-9]+/ },
password : function(v) { return true; }
}
} );
Кстати да. Сейчас там такое есть (в JS не работает):
Почему FiraCode выглядит нереально убого? Это «что-то» сверх-расфокусированное, от чего глаза почти сразу кровью течь хотят. Или у людей это «четкий» шрифт?
Это не от FiraCode зависит, насколько я понимаю, а от вашего экрана. Я привёл скриншот, и если он бьёт вам по глазам, то рискую предположить, что установи вы его себе на компьютер, у вас он будет выглядеть иначе. Т.к. это зависит от ОС и её настроек. А ещё хочу отметить, что шрифты на картинках выглядят часто непривычно или даже не красиво, а будь вы рядом с конкретным монитором, где эта картинка была сделана, вам бы оно могло показаться очень даже ничего. Всё дело в DPI. У меня 3 монитора (мне так удобнее) с разными DPI и на всех 3 всё выглядит немного иначе. Бывает люди подгоняют себе такой DPI и монитор, что им комфортно использовать 1px шрифты (когда все линии равны аккуратно 1px), и в других условиях оно выглядит просто ужасно, а у них всё симпатично.
В общем шрифты это дико холиварная тема. Скажем у меня здесь на хабре стоит 130% zoom и все картинки выглядят мылом. Но зато всё большое и читать приятнее. Компромисс :)
Я привёл скриншот, и если он бьёт вам по глазам
o_O… пардон, из-за диагональных текстов думал, что это «просто картинка». Нет, на ней как раз все выглядит отменно. Плохо же выглядит в IDE, где я естественно, первым делом, и побежал смотреть. Увидев и разочаровавшись, пошел тестировать на машине коллеги. Там так же «не очень»/«сплошное мыло».
Всё дело в DPI.
Ок, это многое объясняет, не подумал сперва — виноват.
Есть варианты
Всяких коротких языковых конструкций можно еще в perl`e подсмотреть.
Я никогда не понимал, почему нельзя было сделать обычно, что обращение к undefined возвращает undefinedПотому что, если Вы вдруг обращаетесь к свойствам undefined, то, вероятно, это ошибка. Если тихо пропускать это, это может привести к багам.
Ну и раз появится оператор «?.», то уже нет разницы.
undefined.test Uncaught TypeError: Cannot read property 'a' of undefined
Или вы не понимаете почему оба так работают?
Операторы ?., ?? и |>: будущие возможности JavaScript, которые вам понравятся