From cc7d3de241d5c33faff0754b8c293b672f708482 Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Sat, 25 Apr 2020 19:14:38 +0000 Subject: [PATCH 1/7] =?UTF-8?q?Create=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...st-hands-on-experience-with-reasonml_en.md | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 src/_content/blog/first-hands-on-experience-with-reasonml_en.md diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md new file mode 100644 index 0000000..0a06cc8 --- /dev/null +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -0,0 +1,240 @@ +--- +slug: first-hands-on-experience-with-reasonml +lang: en +title: First hands on experience with ReasonML +date: 2020-04-25T19:09:05.782Z +thumbnail: + author: Robert Bye + img: /images/uploads/surf-robert-bye-unsplash.jpg + src: https://unsplash.com/@robertbye +tags: + - reasonml + - functional_programming + - fp + - bucklescript + - ocaml +preface: Некоторое время назад я начал знакомиться с новым для себя языком + программирования - ocaml, с его синтаксисом ReasonML если быть точным. А чтобы + разобраться в нём получше я решил ещё и писать статьи. Эта будет первой (и + надеюсь не последней). +--- + +## Where did it came from? + +First of all, as offitiall docs stands [reason]() is not a new programming language, it's a new syntax and toolchain powered by the battle-tested language, [OCaml](https://ocaml.org/). + + +Чаще всего о _reason_'е говорят в контексте javascript'а и _reactjs_, и не с проста: наряду с новым синтаксисом разрабатывается ещё и компилятор в js - [bucklescript](https://bucklescript.github.io), а его (_reason_) разработкой руководит [Jordan Walke](https://twitter.com/jordwalke), ранее создавший _react_. + +## Чего от него ждать? Где использовать? + +Повторюсь, _reason_ не новый язык, а синтаксис. Ocaml является языком общего назначения и применяется в самых разных областях (украл с офф сайта): + +- автоматические доказыватели теорем 🤷‍♂️ +- компиляторы и интерпретаторы 🙆🏾 +- анализаторы программ 🔍 +- обучение программированию 👩🏽‍🎓 + +[Тут вот](https://ocaml.org/learn/success.html) можно найти истории успеха его использования, в т.ч. и более прикладные. + +_Reason_ тоже уже активно используется и можно найти интересные и актуальные примеры на нём: + +- [revery](https://github.com/revery-ui/revery) - фреймворк для разработки кросс-платформенных десктопных приложений 💻 +- [onivim](https://v2.onivim.io/) - среда разработки, построенная на 👆 +- [rely](https://reason-native.com/docs/rely/) - jest-like blazing fast native test framework ⚡️ +- ... + +И что будет более интересным для _react_ разработчиков: поговаривают, что _reason_ станет лучшей платформой для реакта, ведь: + +- он функциональный, а значит более идиоматичный для реакта.\ + _js_ тоже можно назвать функциональным языком, но в отличии от _js_ в _reason_ есть: + - [каррирование](https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5) (curring) + - [сопоставление с образцом](https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81_%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D1%86%D0%BE%D0%BC) (pattern matching) + - встроенная _Option_ монада (которая закрывает проблему \`can't read property of underfined\`) + - ... +- статически типизированный, с мощным механизмом выведения типов\ + Опять же тема, казалась бы, не нова для фронтенд сообщества: _typescript_, _flow_ хорошо себя зарекомендовали, я себе не представляю старт проекта без них. Но _reason_ это совершенно новый уровень: в нём объявлять типы практически не нужно, даже для параметров функции!\ + \ + Также reason в отличии от них типизированный как бы изначально. В то время как _typescript_'у и _flow_ приходится бороться/приспосабливаться к динамической природе _js_, н.р. есть такое понятие как писать код, который можно (или легче) типизировать. + \ + Не думаю, что у меня получится раскрыть эту тему лучше, чем у Jordan Walk в этой ветке: + + + +- у него отличный interprop с _js_ - можно использовать _js_ в _reason_ 😲и наоборот _reason_ в _js_ 🙃\ + Результат компиляции bucklescript'ом минималистичный, оптимизированный для производительности и, что не маловажно, читабельный 📖! +- его можно компилировать в машинный код 🤪а это значит вы можете получить максимальную производительность.\ + Особенно это интересно с точки зрения мобильных приложений. Может скоро _js_'у не будет места в _react_ _native_, и мы будем компилировать reason в нативный код? + +## От слов к делу + +Ну теперь, я надеюсь, вы заинтересовались языком, и мы можем попробовать что-нибудь на нём изобразить. Долго думать над приложением, которое мы будем писать, мне не пришлось. Ещё с университетских времён я знаком с [игрой "Жизнь"](https://ru.wikipedia.org/wiki/%D0%98%D0%B3%D1%80%D0%B0_%C2%AB%D0%96%D0%B8%D0%B7%D0%BD%D1%8C%C2%BB). Первую реализацию, как мне кажется, я делал на _pascal_'е, потом был _delphi_, _c++_, _java, js_ и вот теперь _reason_. Я считаю, что реализация этой игры является отличным способом поизучать язык: там и какой-никакой алгоритм нужно реализовать, поработать с коллекциями, написать UI... + +Посмотреть на готовую реализацию и исходный код, кстати, уже можно [здесь](https://kitos.github.io/game-of-life/), т.к. начал я этот проект несколько месяцев назад. Но и то и другое скорее всего будет меняться во время этого цикла статей: надеюсь я буду находить лучшие решения. + +Место действия этой игры - вселенная, предлагаю и начать с её сотворения. + +## Variant! + +Согласно википедии, вселенная у нас - плоскость, каждая клетка которой может иметь одно из двух состояний: живая (заполненная) 💃 и мёртвая (пустая) 🙅‍♂️. + +В _js_, да и во многих других языках, я бы прибегнул к использованию _boolean_ или строки (`'alive' | 'dead'`) для описания этого состояния. Но первый подход, на мой взгляд, не самый прозрачный/декларативный, а второй мне не нравится тем, что строки и без того многолики: они и текст хранят, и ключами в объектах/картах выступают и наверняка что-то ещё 🤔. В _typescript_ есть _enum_, что уже ближе к тому, с чем хотелось бы работать. Но в _reason_ есть нечто лучшее - _variant_'ы. Чем они лучше? + +1. Они дают бОльшую типо-безопасность - _reason_ заставляет проверить все кейсы _variant_'ов при работе с ними. Встроеным variant'ом является `option`, тот самый, что спасёт нас от `null`'ов и `undefined`'ов, которых в reason нет. В случае с `option` значение у нас может быть - `Some('a)` или не быть - `None`. +2. Они могут хранить одно или несколько значений внутри. +3. Они идут вкупе с другим мощным механизмом языка - сопоставлением с образцом (pattern matching) + +Пока давайте остановимся на том, что объявим наш _variant_: + +```reason +type cellState = + | Dead + | Alive; + +let myCell = Alive; +``` + +Наглядно, не правда ли? + +Теперь давайте создадим вселенную - плоскость. Для этого, очевидно, мы будем использовать двумерный массив. Т.к. как цель статьи - изучения языка, давайте попробуем сами реализовать функции, необходимые для создания массивов. + +Т.к. двумерный массив ничто иное как массив массивов, начнём мы с функции создания массива, причём не пустого, а наполненного необходимыми значениями. Т.е. я хочу написать функцию, которая принимает 2 значения: длину создаваемого массива и функцию _инициализатор_, которая будет получать в качестве аргумента индекс инициализируемого элемента (обычно это очень удобно, далее покажу почему). Такие функции в функциональном программировании (далее просто _ФП_) носят называние функций высшего порядка, т.к. в качестве аргументов они принимают другие функции (или возвращают их). + +## Рекурсивное создание массива + +![Recursion](/images/uploads/recursion.jpg 'Recursion') + +Другой особенностью языков ФП является то, что в них как правило отсутствуют операторы управления потоком исполнения (🤯): if/else, for/while... т.к. они уводят разработчика от описания вычислений в описание команд машине (не придумал как лучше выразиться 🤷‍♂️). Да и вообще как заметил в одном из своих докладов [Виталий Брагилевский](https://twitter.com/_bravit) ни чем не лучше всеми ненавистного оператора `goto`. И не смотря на то, что в _reason_ [циклы есть](https://reasonml.github.io/docs/en/imperative-loops), мы попытаемся воспользоваться идеоматической конструкцией - рекурсией. + +```reason +let init = (l, fn) => { + /* + * на каждом шаге рекурсии + * добавляем новый элемент массива + * и передаём результат в следующий шаг + */ + let rec _init = (l, arr) => + l > 0 ? _init(l - 1, concat(arr, [|fn(l)|])) : arr; + + _init(l, [||]); +}; +``` + +Для людей пришедших из _js_, незнакомыми тут являются 2 конструкции: + +- `rec` - в _reason_ мы вынуждены явно указать, что наша функция будет рекурсивной +- `reason±[||]` - литерал создания массива (более привычный `js±[]` создаст список) + +### Tail call optimisation + +Кто-то, возможно, заметит, что эту функцию можно было бы реализовать и без дополнительной внутренней, а кто-то, что создавать массивы рекурсивно не комильфо - ведь мы можем переполнить стек. И как ни странно оба эти утверждения связаны 😮. + +Да, я действительно мог записать эту функцию несколько короче: + +```reason +let rec init = (l, fn) => + l > 0 ? concat(init(l - 1, fn), [|fn(l)|]) : [||]; +``` + +Но именно эта реализация и привела бы к потенциальному переполнению стека 😆. Почему? Функциональные языки программирования появились не вчера, и компиляторы умеют хорошо их оптимизировать. Примером такой оптимизации является _tail call_. Если коротко, то суть в том, что можно не создавать фрейм в стеке для рекурсивного вызова, если он является последним действием в функции. За счёт этого производительность такой рекурсии будет равна итерации (и вообще может быть ей заменена компилятором). Поподробнее об этом можно прочитать [здесь](https://www.webpurple.net/blog/2018-07-19-optimizaciya-hvostovyh-vyzovov-v-ecmascript-6/). + +И если посмотреть на первую реализацию, то видно что в функции `_init` её рекурсивный вызов и является последним, а значит наш компилятор должен её оптимизировать 😌. + +И тут искушённый читатель может заметить, что не смотря на то, что ECMAScript 6 предлагает tail call оптимизацию, [большинство браузеров реализовать её пока не смогли](). И он будет прав 😭. Но! _Reason_ ведь не _js_, и у него есть свой компилятор - _BuckleScript_. И он не полагается на интерпретаторы _js_ и сам реализует эту оптимизацию, заменяя эту контрукцию на `while` 🤪. Так будет выглядить скомпилированная функция: + +```js +function init(l, fn) { + var _l = l + var _arr = /* array */ [] + while (true) { + var arr = _arr + var l$1 = _l + var match = l$1 > 0 + if (match) { + _arr = /* array */ [Curry._1(fn, (l$1 - 1) | 0)].concat(arr) + _l = (l$1 - 1) | 0 + continue + } else { + return arr + } + } +} +``` + +### Императивность под капотом декларативности + +![Scooby doo mask reveal](/images/uploads/reveal.jpg 'Reveal') + +Ещё одним замечанием, которое напрашивается, является конкатенация массивов на каждом шаге. И не смотря на то, что я являюсь противником преждевременных оптимизаций, не обратить на это внимание в статье я не могу. Мы действительно создаём массив из одного элемента, а потом копируем его и массив полученный на предыдущем шаге в новый массив. На создание массива из _10к_ элеметов наша функция тратит _~60ms_ на моём macbook pro. И с этим магический компилятор _reason_'а уже ничего поделать не может. Но и не должен! + +- Данная функция лишь пример того, как мы можем использовать рекурсию вместо императивных циклов +- _Reason_ не запрещает использовать циклы +- Также можно использовать более идеоматические для ФП структуры данных.\ + Например использование списков в рекурсивной функции, а затем преобразование этого списка в массив позволяет создать массив из миллиона элементов за _~300ms_ (ведь добавление элемента в список очень дешёвое) +- У _reason_ есть стандартная библиотека _Belt_ которая предоставляет аналогичный метод `makeBy`, который смог создать массив из _10млн_ элеметов с теже _~60ms_ + +Он, кстати, под капотом имеет ту самую императивщину: + +```js +function makeByU(l, f) { + if (l <= 0) { + return /* array */ [] + } else { + var res = new Array(l) + for (var i = 0, i_finish = (l - 1) | 0; i <= i_finish; ++i) { + res[i] = f(i) + } + return res + } +} +``` + +### Типизация + +А вы заметили, что мы не описали ни одного типа? Но reason при этом вывел следующий тип для нашей функции: `reason±(int, int => 'a) => Js_array.t('a)`, что означает: фунция которая на вход принимает целое число и функцию, которая тоже принимает целое число а возвращает _что-то_ (`a'`), а наша исходная функция вернёт массив этого _чего-то_. Так мы с вами это и задумывали: целое число - это размер создаваемого массива, функция - наш инициализатор, ну и результат действительно массив результатов инициализатора. Кстати, плагин для _WebStorm_ показывает выведенные типы следующим образом: + +![Webstorm plugin displays inferred types](/images/uploads/webstorm-reason-inference.jpg 'Webstorm plugin for reasonml') + +Магия не правда ли? Для меня, как для человека большую часть времени работающего с _typescript_'ом - правда. Как это работает? Ну... _Reason_ это ведь новый синтаксис для _OCaml_, который является реализацией языка _Caml_, который, в свою очередь, принадлежит семейству _ML_, в котором используется система типов _Хиндли—Милнера_ (надеюсь не ошибся в этой цепочке 🤞). Именно эта система то и позволяет автоматически выводить типы для выражений. Из теории это всё, что на данный момент могу сказать 😳. На практике же, как вы убедились, это означает что нам вовсе не надо описывать типы чтобы получить статическую типизацию. + +## Создание матрицы + +![Neo stands in front of matrix of TVs](/images/uploads/matrix.jpg 'Matrix') + +Ну теперь когда мы умеем создавать массивы, можно и создать массив массивов. Для этого предлагаю использовать библиотечную функцию `reason±makeBy` которая сигнатурой не отличается от созданной нами, но на несколько порядков производительнее: + +```reason +let makeMatrixBy = (dimx, dimy, fn) => + makeBy(dimy, y => makeBy(dimx, x => fn((x, y)))); +``` + +Ну тут вроде всё просто: принимаем размеры матрицы и функцию инициализатор, возвращаем массив размером `reason±dimy` каждый элемент которого это массив размером `reason±dimx`, наполненный значениями которые возвращает функция `reason±fn`. + +### Tuple + +Но особенность тут всё же есть - `reason±fn((x, y))`. Я не ошибся, это не лишние скобочки, это [кортеж]() ([tuple](https://en.wikipedia.org/wiki/Tuple)) - неизменяемый, упорядоченный, разнородный список значений фиксированного размера. Как говорит официальная документация эта структура данных очень удобна, когда вам нужно передать или вернуть несколько значений _без лишних церемоний_. + +Ну с неизменяемостью и фиксированным размером, я думаю, всё понятно. Упорядоченность элементов позволяет обойтись без имён свойств. А благодаря разнородности мы можем хранить значения разных типов в одном кортеже. + +Т.е. _tuple_ может заменить нам объекты, но я бы не стал их использовать для сущности с более чем тремя атрибутами - сложно будет запомнить, что зачем идёт. А вот для хранения пары координат они подходят идеально 😉. + +В _js_, кстати, аналогом могут выступить массивы, для них (как и в _reason_ для _tuple_) есть очень удобный синтаксис деструктуризации: `js±let [x, y] = coordinates;` (в reason: `reason±let (x, y) = coordinates;`). + +Ну давайте наконец попробуем создать случайно заполненную вселенную: + +```reason +let universe = makeMatrixBy(5, 5, _ => Js_math.random() > 0.5 ? Alive : Dead); +Js.log(universe); +``` + +Что в консоли хрома даёт нам вот такую картинку: + +![Матрица в консоли хрома](/images/uploads/random-universe.jpg 'Матрица') + +## Итого + +Не смотря на то, что с точки зрения игры мы продвинулись не так далеко, мы теперь знаем что такое _reason_, откуда он пришёл и где его можно использовать, а также разобрали несколько языковых конструкций. + +Надеюсь, вам было интересно, и вы ещё придёте читать другие статьи про _reason_ и не только. + +А ещё не забывайте давать обратную связь. Сделать это можно через комментарии, а можно выделив текст на странице отправить сообщение о какой-либо неточности. From a29fe02004b29f0edfea3be2c61a5db44e8370b2 Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Sun, 26 Apr 2020 08:28:59 +0000 Subject: [PATCH 2/7] =?UTF-8?q?Update=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blog/first-hands-on-experience-with-reasonml_en.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md index 0a06cc8..0f2f6f6 100644 --- a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -23,8 +23,7 @@ preface: Некоторое время назад я начал знакомит First of all, as offitiall docs stands [reason]() is not a new programming language, it's a new syntax and toolchain powered by the battle-tested language, [OCaml](https://ocaml.org/). - -Чаще всего о _reason_'е говорят в контексте javascript'а и _reactjs_, и не с проста: наряду с новым синтаксисом разрабатывается ещё и компилятор в js - [bucklescript](https://bucklescript.github.io), а его (_reason_) разработкой руководит [Jordan Walke](https://twitter.com/jordwalke), ранее создавший _react_. +Currently _reason_ is mostly mentioned in _javascript_ and _react_ communities for a reason 😏: along with new syntax, a new _backend_ (compiler to _js_) is being developed - [bucklescript](https://bucklescript.github.io), and it is led by [Jordan Walke](https://twitter.com/jordwalke), who previously created _react_. ## Чего от него ждать? Где использовать? From 5b9c70834187c493985bb9fc4bd7002ac0ec128d Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Sun, 26 Apr 2020 08:34:35 +0000 Subject: [PATCH 3/7] =?UTF-8?q?Update=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...first-hands-on-experience-with-reasonml_en.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md index 0f2f6f6..ac7235c 100644 --- a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -5,7 +5,7 @@ title: First hands on experience with ReasonML date: 2020-04-25T19:09:05.782Z thumbnail: author: Robert Bye - img: /images/uploads/surf-robert-bye-unsplash.jpg + img: /images/uploads/matrix.jpg src: https://unsplash.com/@robertbye tags: - reasonml @@ -25,14 +25,14 @@ First of all, as offitiall docs stands [reason]( Date: Sun, 26 Apr 2020 08:39:56 +0000 Subject: [PATCH 4/7] =?UTF-8?q?Update=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blog/first-hands-on-experience-with-reasonml_en.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md index ac7235c..522b163 100644 --- a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -34,12 +34,12 @@ Like I sad, _reason_ is not a new language, it is a syntax. And OCaml is a gener - used for teaching programming 👩🏽‍🎓 - ... -[Тут вот](https://ocaml.org/learn/success.html) можно найти истории успеха его использования, в т.ч. и более прикладные. +[Here](https://ocaml.org/learn/success.html) you can find a list of success stories. -_Reason_ тоже уже активно используется и можно найти интересные и актуальные примеры на нём: +_Reason_ is also already actively used and there are some pretty interesting projects: -- [revery](https://github.com/revery-ui/revery) - фреймворк для разработки кросс-платформенных десктопных приложений 💻 -- [onivim](https://v2.onivim.io/) - среда разработки, построенная на 👆 +- [revery](https://github.com/revery-ui/revery) - framework for development of cross-platform desktop apps 💻 +- [onivim](https://v2.onivim.io/) - IDE build with _revery_ 👆 - [rely](https://reason-native.com/docs/rely/) - jest-like blazing fast native test framework ⚡️ - ... From 1efe2f3fd3da05025a22cbdc260ffccc029ce1ea Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Sun, 26 Apr 2020 10:23:07 +0000 Subject: [PATCH 5/7] =?UTF-8?q?Update=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blog/first-hands-on-experience-with-reasonml_en.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md index 522b163..851d976 100644 --- a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -5,7 +5,7 @@ title: First hands on experience with ReasonML date: 2020-04-25T19:09:05.782Z thumbnail: author: Robert Bye - img: /images/uploads/matrix.jpg + img: /images/uploads/surf-robert-bye-unsplash.jpg src: https://unsplash.com/@robertbye tags: - reasonml @@ -43,7 +43,7 @@ _Reason_ is also already actively used and there are some pretty interesting pro - [rely](https://reason-native.com/docs/rely/) - jest-like blazing fast native test framework ⚡️ - ... -И что будет более интересным для _react_ разработчиков: поговаривают, что _reason_ станет лучшей платформой для реакта, ведь: +And I think it will be especially interesting for _react_ developers to hear that _reason_ is [gonna be a better platform for _react_](https://youtu.be/5fG_lyNuEAw): - он функциональный, а значит более идиоматичный для реакта.\ _js_ тоже можно назвать функциональным языком, но в отличии от _js_ в _reason_ есть: From 016b6a022476cac50a6567c82e8bcc692b09a792 Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Sun, 26 Apr 2020 13:34:18 +0000 Subject: [PATCH 6/7] =?UTF-8?q?Update=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...st-hands-on-experience-with-reasonml_en.md | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md index 851d976..e21ba5a 100644 --- a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -45,29 +45,27 @@ _Reason_ is also already actively used and there are some pretty interesting pro And I think it will be especially interesting for _react_ developers to hear that _reason_ is [gonna be a better platform for _react_](https://youtu.be/5fG_lyNuEAw): -- он функциональный, а значит более идиоматичный для реакта.\ - _js_ тоже можно назвать функциональным языком, но в отличии от _js_ в _reason_ есть: - - [каррирование](https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D1%80%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5) (curring) - - [сопоставление с образцом](https://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5_%D1%81_%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D1%86%D0%BE%D0%BC) (pattern matching) - - встроенная _Option_ монада (которая закрывает проблему \`can't read property of underfined\`) +- its syntax should be familiar to _js_ developers +- it is functional hence it is more idiomatic for react.\ + _js_ can also be considered functional language, but unlike _js_ in _reason_ there are: + - [carring](https://en.wikipedia.org/wiki/Currying) + - [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching) + - build in _Option_ monad (which it wipes out \`can't read property of underfined\` problem an more) - ... -- статически типизированный, с мощным механизмом выведения типов\ - Опять же тема, казалась бы, не нова для фронтенд сообщества: _typescript_, _flow_ хорошо себя зарекомендовали, я себе не представляю старт проекта без них. Но _reason_ это совершенно новый уровень: в нём объявлять типы практически не нужно, даже для параметров функции!\ +- statically typed, with strong inference mechanism\ + Even though it might not sound like a new think to you: both _typescript_ and _flow_ proved themselves, and I personally can not imagine starting a new project without them. But _reason_ is at completely another level: e.g. you don't have to declare types even for function arguments (which is also true for _flow_). And what even more important, _reason_ was built with inference in mind. While _typescript_ and _flow_ have to struggle with dynamic nature of _js_. \ - Также reason в отличии от них типизированный как бы изначально. В то время как _typescript_'у и _flow_ приходится бороться/приспосабливаться к динамической природе _js_, н.р. есть такое понятие как писать код, который можно (или легче) типизировать. - \ - Не думаю, что у меня получится раскрыть эту тему лучше, чем у Jordan Walk в этой ветке: + Obviously these benefits don't come for free: some things, which people are used to have in _js_, are not available in _reason. And here are some thoughts from Jordan about it: -- у него отличный interprop с _js_ - можно использовать _js_ в _reason_ 😲и наоборот _reason_ в _js_ 🙃\ - Результат компиляции bucklescript'ом минималистичный, оптимизированный для производительности и, что не маловажно, читабельный 📖! -- его можно компилировать в машинный код 🤪а это значит вы можете получить максимальную производительность.\ - Особенно это интересно с точки зрения мобильных приложений. Может скоро _js_'у не будет места в _react_ _native_, и мы будем компилировать reason в нативный код? +- it has awesome interprop with _js_ - you can use _js_ in _reason 😲 and visa-versa 🙃\ + Result of compilation is minimalistic, optimised for perf and last, but not least human readable 📖! +- it can be compiled to native code, hence you can get insane performance if you need it -## От слов к делу +## All right, down to business -Ну теперь, я надеюсь, вы заинтересовались языком, и мы можем попробовать что-нибудь на нём изобразить. Долго думать над приложением, которое мы будем писать, мне не пришлось. Ещё с университетских времён я знаком с [игрой "Жизнь"](https://ru.wikipedia.org/wiki/%D0%98%D0%B3%D1%80%D0%B0_%C2%AB%D0%96%D0%B8%D0%B7%D0%BD%D1%8C%C2%BB). Первую реализацию, как мне кажется, я делал на _pascal_'е, потом был _delphi_, _c++_, _java, js_ и вот теперь _reason_. Я считаю, что реализация этой игры является отличным способом поизучать язык: там и какой-никакой алгоритм нужно реализовать, поработать с коллекциями, написать UI... +I hope, at this point you are impressed and interested in learning this language. So we can try to build something using it. I didn't have to think a lot about the app I want to build here. Since uni I am familiar with a [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life). I think I implemented it first in _Pascal_, then in _Delphi_, _c_, _java_, _js_ and apparently it is time to write it in _reason_. I believe it is really good way to start learning a language: you have to write some algorithm, work with collections, build some UI... Посмотреть на готовую реализацию и исходный код, кстати, уже можно [здесь](https://kitos.github.io/game-of-life/), т.к. начал я этот проект несколько месяцев назад. Но и то и другое скорее всего будет меняться во время этого цикла статей: надеюсь я буду находить лучшие решения. From ca3137d539e293bd5207f7573ef2c579517bd33e Mon Sep 17 00:00:00 2001 From: Nikita Kirsanov Date: Sun, 26 Apr 2020 13:38:24 +0000 Subject: [PATCH 7/7] =?UTF-8?q?Update=20Blog=20=E2=80=9Cfirst-hands-on-exp?= =?UTF-8?q?erience-with-reasonml=5Fen=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...st-hands-on-experience-with-reasonml_en.md | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md index e21ba5a..58f7465 100644 --- a/src/_content/blog/first-hands-on-experience-with-reasonml_en.md +++ b/src/_content/blog/first-hands-on-experience-with-reasonml_en.md @@ -18,56 +18,56 @@ preface: Некоторое время назад я начал знакомит разобраться в нём получше я решил ещё и писать статьи. Эта будет первой (и надеюсь не последней). --- - ## Where did it came from? -First of all, as offitiall docs stands [reason]() is not a new programming language, it's a new syntax and toolchain powered by the battle-tested language, [OCaml](https://ocaml.org/). +First of all, as offitiall docs stands [reason](https://en.wikipedia.org/wiki/Reason_(programming_language)) is not a new programming language, it's a new syntax and toolchain powered by the battle-tested language, [OCaml](https://ocaml.org/). -Currently _reason_ is mostly mentioned in _javascript_ and _react_ communities for a reason 😏: along with new syntax, a new _backend_ (compiler to _js_) is being developed - [bucklescript](https://bucklescript.github.io), and it is led by [Jordan Walke](https://twitter.com/jordwalke), who previously created _react_. +Currently *reason* is mostly mentioned in *javascript* and *react* communities for a reason 😏: along with new syntax, a new *backend* (compiler to *js*) is being developed - [bucklescript](https://bucklescript.github.io), and it is led by [Jordan Walke](https://twitter.com/jordwalke), who previously created *react*. ## What can we expect? Where can we use it? -Like I sad, _reason_ is not a new language, it is a syntax. And OCaml is a general purpose language, so it is applied in various areas: -- automatic theorem provers 🤷‍♂️ -- compilers and interpreters 🙆🏾 -- program analyzers 🙆🏾 -- used for teaching programming 👩🏽‍🎓 -- ... +Like I sad, *reason* is not a new language, it is a syntax. And OCaml is a general purpose language, so it is applied in various areas: + +* automatic theorem provers 🤷‍♂️ +* compilers and interpreters 🙆🏾 +* program analyzers 🙆🏾 +* used for teaching programming 👩🏽‍🎓 +* ... [Here](https://ocaml.org/learn/success.html) you can find a list of success stories. -_Reason_ is also already actively used and there are some pretty interesting projects: +*Reason* is also already actively used and there are some pretty interesting projects: + +* [revery](https://github.com/revery-ui/revery) - framework for development of cross-platform desktop apps 💻 +* [onivim](https://v2.onivim.io/) - IDE build with *revery* 👆 +* [rely](https://reason-native.com/docs/rely/) - jest-like blazing fast native test framework ⚡️ +* ... -- [revery](https://github.com/revery-ui/revery) - framework for development of cross-platform desktop apps 💻 -- [onivim](https://v2.onivim.io/) - IDE build with _revery_ 👆 -- [rely](https://reason-native.com/docs/rely/) - jest-like blazing fast native test framework ⚡️ -- ... +And I think it will be especially interesting for *react* developers to hear that *reason* is [gonna be a better platform for *react*](https://youtu.be/5fG_lyNuEAw): -And I think it will be especially interesting for _react_ developers to hear that _reason_ is [gonna be a better platform for _react_](https://youtu.be/5fG_lyNuEAw): +* its syntax should be familiar to *js* developers +* it is functional hence it is more idiomatic for react.\ + *js* can also be considered functional language, but unlike *js* in *reason* there are: -- its syntax should be familiar to _js_ developers -- it is functional hence it is more idiomatic for react.\ - _js_ can also be considered functional language, but unlike _js_ in _reason_ there are: - - [carring](https://en.wikipedia.org/wiki/Currying) - - [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching) - - build in _Option_ monad (which it wipes out \`can't read property of underfined\` problem an more) - - ... -- statically typed, with strong inference mechanism\ - Even though it might not sound like a new think to you: both _typescript_ and _flow_ proved themselves, and I personally can not imagine starting a new project without them. But _reason_ is at completely another level: e.g. you don't have to declare types even for function arguments (which is also true for _flow_). And what even more important, _reason_ was built with inference in mind. While _typescript_ and _flow_ have to struggle with dynamic nature of _js_. - \ - Obviously these benefits don't come for free: some things, which people are used to have in _js_, are not available in _reason. And here are some thoughts from Jordan about it: + * [carring](https://en.wikipedia.org/wiki/Currying) + * [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching) + * build in *Option* monad (which it wipes out \`can't read property of underfined\` problem an more) + * ... +* statically typed, with strong inference mechanism\ + Even though it might not sound like a new think to you: both *typescript* and *flow* proved themselves, and I personally can not imagine starting a new project without them. But *reason* is at completely another level: e.g. you don't have to declare types even for function arguments (which is also true for *flow*). And what even more important, *reason* was built with inference in mind. While *typescript* and *flow* have to struggle with dynamic nature of *js*. \ + Obviously these benefits don't come for free: some things, which people are used to have in *js*, are not available in _reason. And here are some thoughts from Jordan about it: -- it has awesome interprop with _js_ - you can use _js_ in _reason 😲 and visa-versa 🙃\ +* it has awesome interprop with *js* - you can use *js* in _reason 😲 and visa-versa 🙃\ Result of compilation is minimalistic, optimised for perf and last, but not least human readable 📖! -- it can be compiled to native code, hence you can get insane performance if you need it +* it can be compiled to native code, hence you can get insane performance if you need it ## All right, down to business -I hope, at this point you are impressed and interested in learning this language. So we can try to build something using it. I didn't have to think a lot about the app I want to build here. Since uni I am familiar with a [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life). I think I implemented it first in _Pascal_, then in _Delphi_, _c_, _java_, _js_ and apparently it is time to write it in _reason_. I believe it is really good way to start learning a language: you have to write some algorithm, work with collections, build some UI... +I hope, at this point you are impressed and interested in learning this language. So we can try to build something using it. I didn't have to think a lot about the app I want to build here. Since uni I am familiar with a [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life). I think I implemented it first in *Pascal*, then in *Delphi*, *c*, *java*, *js* and, apparently, it is time to write it in *reason*. I believe it is really good way to start learning a language: you have to write some algorithm, work with collections, build some UI... -Посмотреть на готовую реализацию и исходный код, кстати, уже можно [здесь](https://kitos.github.io/game-of-life/), т.к. начал я этот проект несколько месяцев назад. Но и то и другое скорее всего будет меняться во время этого цикла статей: надеюсь я буду находить лучшие решения. +You can already checkout complete game and it's source [here](https://kitos.github.io/game-of-life/). Место действия этой игры - вселенная, предлагаю и начать с её сотворения. @@ -75,13 +75,13 @@ I hope, at this point you are impressed and interested in learning this language Согласно википедии, вселенная у нас - плоскость, каждая клетка которой может иметь одно из двух состояний: живая (заполненная) 💃 и мёртвая (пустая) 🙅‍♂️. -В _js_, да и во многих других языках, я бы прибегнул к использованию _boolean_ или строки (`'alive' | 'dead'`) для описания этого состояния. Но первый подход, на мой взгляд, не самый прозрачный/декларативный, а второй мне не нравится тем, что строки и без того многолики: они и текст хранят, и ключами в объектах/картах выступают и наверняка что-то ещё 🤔. В _typescript_ есть _enum_, что уже ближе к тому, с чем хотелось бы работать. Но в _reason_ есть нечто лучшее - _variant_'ы. Чем они лучше? +В *js*, да и во многих других языках, я бы прибегнул к использованию *boolean* или строки (`'alive' | 'dead'`) для описания этого состояния. Но первый подход, на мой взгляд, не самый прозрачный/декларативный, а второй мне не нравится тем, что строки и без того многолики: они и текст хранят, и ключами в объектах/картах выступают и наверняка что-то ещё 🤔. В *typescript* есть *enum*, что уже ближе к тому, с чем хотелось бы работать. Но в *reason* есть нечто лучшее - *variant*'ы. Чем они лучше? -1. Они дают бОльшую типо-безопасность - _reason_ заставляет проверить все кейсы _variant_'ов при работе с ними. Встроеным variant'ом является `option`, тот самый, что спасёт нас от `null`'ов и `undefined`'ов, которых в reason нет. В случае с `option` значение у нас может быть - `Some('a)` или не быть - `None`. +1. Они дают бОльшую типо-безопасность - *reason* заставляет проверить все кейсы *variant*'ов при работе с ними. Встроеным variant'ом является `option`, тот самый, что спасёт нас от `null`'ов и `undefined`'ов, которых в reason нет. В случае с `option` значение у нас может быть - `Some('a)` или не быть - `None`. 2. Они могут хранить одно или несколько значений внутри. 3. Они идут вкупе с другим мощным механизмом языка - сопоставлением с образцом (pattern matching) -Пока давайте остановимся на том, что объявим наш _variant_: +Пока давайте остановимся на том, что объявим наш *variant*: ```reason type cellState = @@ -95,13 +95,13 @@ let myCell = Alive; Теперь давайте создадим вселенную - плоскость. Для этого, очевидно, мы будем использовать двумерный массив. Т.к. как цель статьи - изучения языка, давайте попробуем сами реализовать функции, необходимые для создания массивов. -Т.к. двумерный массив ничто иное как массив массивов, начнём мы с функции создания массива, причём не пустого, а наполненного необходимыми значениями. Т.е. я хочу написать функцию, которая принимает 2 значения: длину создаваемого массива и функцию _инициализатор_, которая будет получать в качестве аргумента индекс инициализируемого элемента (обычно это очень удобно, далее покажу почему). Такие функции в функциональном программировании (далее просто _ФП_) носят называние функций высшего порядка, т.к. в качестве аргументов они принимают другие функции (или возвращают их). +Т.к. двумерный массив ничто иное как массив массивов, начнём мы с функции создания массива, причём не пустого, а наполненного необходимыми значениями. Т.е. я хочу написать функцию, которая принимает 2 значения: длину создаваемого массива и функцию *инициализатор*, которая будет получать в качестве аргумента индекс инициализируемого элемента (обычно это очень удобно, далее покажу почему). Такие функции в функциональном программировании (далее просто *ФП*) носят называние функций высшего порядка, т.к. в качестве аргументов они принимают другие функции (или возвращают их). ## Рекурсивное создание массива ![Recursion](/images/uploads/recursion.jpg 'Recursion') -Другой особенностью языков ФП является то, что в них как правило отсутствуют операторы управления потоком исполнения (🤯): if/else, for/while... т.к. они уводят разработчика от описания вычислений в описание команд машине (не придумал как лучше выразиться 🤷‍♂️). Да и вообще как заметил в одном из своих докладов [Виталий Брагилевский](https://twitter.com/_bravit) ни чем не лучше всеми ненавистного оператора `goto`. И не смотря на то, что в _reason_ [циклы есть](https://reasonml.github.io/docs/en/imperative-loops), мы попытаемся воспользоваться идеоматической конструкцией - рекурсией. +Другой особенностью языков ФП является то, что в них как правило отсутствуют операторы управления потоком исполнения (🤯): if/else, for/while... т.к. они уводят разработчика от описания вычислений в описание команд машине (не придумал как лучше выразиться 🤷‍♂️). Да и вообще как заметил в одном из своих докладов [Виталий Брагилевский](https://twitter.com/_bravit) ни чем не лучше всеми ненавистного оператора `goto`. И не смотря на то, что в *reason* [циклы есть](https://reasonml.github.io/docs/en/imperative-loops), мы попытаемся воспользоваться идеоматической конструкцией - рекурсией. ```reason let init = (l, fn) => { @@ -117,10 +117,10 @@ let init = (l, fn) => { }; ``` -Для людей пришедших из _js_, незнакомыми тут являются 2 конструкции: +Для людей пришедших из *js*, незнакомыми тут являются 2 конструкции: -- `rec` - в _reason_ мы вынуждены явно указать, что наша функция будет рекурсивной -- `reason±[||]` - литерал создания массива (более привычный `js±[]` создаст список) +* `rec` - в *reason* мы вынуждены явно указать, что наша функция будет рекурсивной +* `reason±[||]` - литерал создания массива (более привычный `js±[]` создаст список) ### Tail call optimisation @@ -133,11 +133,11 @@ let rec init = (l, fn) => l > 0 ? concat(init(l - 1, fn), [|fn(l)|]) : [||]; ``` -Но именно эта реализация и привела бы к потенциальному переполнению стека 😆. Почему? Функциональные языки программирования появились не вчера, и компиляторы умеют хорошо их оптимизировать. Примером такой оптимизации является _tail call_. Если коротко, то суть в том, что можно не создавать фрейм в стеке для рекурсивного вызова, если он является последним действием в функции. За счёт этого производительность такой рекурсии будет равна итерации (и вообще может быть ей заменена компилятором). Поподробнее об этом можно прочитать [здесь](https://www.webpurple.net/blog/2018-07-19-optimizaciya-hvostovyh-vyzovov-v-ecmascript-6/). +Но именно эта реализация и привела бы к потенциальному переполнению стека 😆. Почему? Функциональные языки программирования появились не вчера, и компиляторы умеют хорошо их оптимизировать. Примером такой оптимизации является *tail call*. Если коротко, то суть в том, что можно не создавать фрейм в стеке для рекурсивного вызова, если он является последним действием в функции. За счёт этого производительность такой рекурсии будет равна итерации (и вообще может быть ей заменена компилятором). Поподробнее об этом можно прочитать [здесь](https://www.webpurple.net/blog/2018-07-19-optimizaciya-hvostovyh-vyzovov-v-ecmascript-6/). И если посмотреть на первую реализацию, то видно что в функции `_init` её рекурсивный вызов и является последним, а значит наш компилятор должен её оптимизировать 😌. -И тут искушённый читатель может заметить, что не смотря на то, что ECMAScript 6 предлагает tail call оптимизацию, [большинство браузеров реализовать её пока не смогли](). И он будет прав 😭. Но! _Reason_ ведь не _js_, и у него есть свой компилятор - _BuckleScript_. И он не полагается на интерпретаторы _js_ и сам реализует эту оптимизацию, заменяя эту контрукцию на `while` 🤪. Так будет выглядить скомпилированная функция: +И тут искушённый читатель может заметить, что не смотря на то, что ECMAScript 6 предлагает tail call оптимизацию, [большинство браузеров реализовать её пока не смогли](https://kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation)). И он будет прав 😭. Но! *Reason* ведь не *js*, и у него есть свой компилятор - *BuckleScript*. И он не полагается на интерпретаторы *js* и сам реализует эту оптимизацию, заменяя эту контрукцию на `while` 🤪. Так будет выглядить скомпилированная функция: ```js function init(l, fn) { @@ -162,13 +162,13 @@ function init(l, fn) { ![Scooby doo mask reveal](/images/uploads/reveal.jpg 'Reveal') -Ещё одним замечанием, которое напрашивается, является конкатенация массивов на каждом шаге. И не смотря на то, что я являюсь противником преждевременных оптимизаций, не обратить на это внимание в статье я не могу. Мы действительно создаём массив из одного элемента, а потом копируем его и массив полученный на предыдущем шаге в новый массив. На создание массива из _10к_ элеметов наша функция тратит _~60ms_ на моём macbook pro. И с этим магический компилятор _reason_'а уже ничего поделать не может. Но и не должен! +Ещё одним замечанием, которое напрашивается, является конкатенация массивов на каждом шаге. И не смотря на то, что я являюсь противником преждевременных оптимизаций, не обратить на это внимание в статье я не могу. Мы действительно создаём массив из одного элемента, а потом копируем его и массив полученный на предыдущем шаге в новый массив. На создание массива из *10к* элеметов наша функция тратит *~60ms* на моём macbook pro. И с этим магический компилятор *reason*'а уже ничего поделать не может. Но и не должен! -- Данная функция лишь пример того, как мы можем использовать рекурсию вместо императивных циклов -- _Reason_ не запрещает использовать циклы -- Также можно использовать более идеоматические для ФП структуры данных.\ - Например использование списков в рекурсивной функции, а затем преобразование этого списка в массив позволяет создать массив из миллиона элементов за _~300ms_ (ведь добавление элемента в список очень дешёвое) -- У _reason_ есть стандартная библиотека _Belt_ которая предоставляет аналогичный метод `makeBy`, который смог создать массив из _10млн_ элеметов с теже _~60ms_ +* Данная функция лишь пример того, как мы можем использовать рекурсию вместо императивных циклов +* *Reason* не запрещает использовать циклы +* Также можно использовать более идеоматические для ФП структуры данных.\ + Например использование списков в рекурсивной функции, а затем преобразование этого списка в массив позволяет создать массив из миллиона элементов за *~300ms* (ведь добавление элемента в список очень дешёвое) +* У *reason* есть стандартная библиотека *Belt* которая предоставляет аналогичный метод `makeBy`, который смог создать массив из *10млн* элеметов с теже *~60ms* Он, кстати, под капотом имеет ту самую императивщину: @@ -188,11 +188,11 @@ function makeByU(l, f) { ### Типизация -А вы заметили, что мы не описали ни одного типа? Но reason при этом вывел следующий тип для нашей функции: `reason±(int, int => 'a) => Js_array.t('a)`, что означает: фунция которая на вход принимает целое число и функцию, которая тоже принимает целое число а возвращает _что-то_ (`a'`), а наша исходная функция вернёт массив этого _чего-то_. Так мы с вами это и задумывали: целое число - это размер создаваемого массива, функция - наш инициализатор, ну и результат действительно массив результатов инициализатора. Кстати, плагин для _WebStorm_ показывает выведенные типы следующим образом: +А вы заметили, что мы не описали ни одного типа? Но reason при этом вывел следующий тип для нашей функции: `reason±(int, int => 'a) => Js_array.t('a)`, что означает: фунция которая на вход принимает целое число и функцию, которая тоже принимает целое число а возвращает *что-то* (`a'`), а наша исходная функция вернёт массив этого *чего-то*. Так мы с вами это и задумывали: целое число - это размер создаваемого массива, функция - наш инициализатор, ну и результат действительно массив результатов инициализатора. Кстати, плагин для *WebStorm* показывает выведенные типы следующим образом: ![Webstorm plugin displays inferred types](/images/uploads/webstorm-reason-inference.jpg 'Webstorm plugin for reasonml') -Магия не правда ли? Для меня, как для человека большую часть времени работающего с _typescript_'ом - правда. Как это работает? Ну... _Reason_ это ведь новый синтаксис для _OCaml_, который является реализацией языка _Caml_, который, в свою очередь, принадлежит семейству _ML_, в котором используется система типов _Хиндли—Милнера_ (надеюсь не ошибся в этой цепочке 🤞). Именно эта система то и позволяет автоматически выводить типы для выражений. Из теории это всё, что на данный момент могу сказать 😳. На практике же, как вы убедились, это означает что нам вовсе не надо описывать типы чтобы получить статическую типизацию. +Магия не правда ли? Для меня, как для человека большую часть времени работающего с *typescript*'ом - правда. Как это работает? Ну... *Reason* это ведь новый синтаксис для *OCaml*, который является реализацией языка *Caml*, который, в свою очередь, принадлежит семейству *ML*, в котором используется система типов *Хиндли—Милнера* (надеюсь не ошибся в этой цепочке 🤞). Именно эта система то и позволяет автоматически выводить типы для выражений. Из теории это всё, что на данный момент могу сказать 😳. На практике же, как вы убедились, это означает что нам вовсе не надо описывать типы чтобы получить статическую типизацию. ## Создание матрицы @@ -209,13 +209,13 @@ let makeMatrixBy = (dimx, dimy, fn) => ### Tuple -Но особенность тут всё же есть - `reason±fn((x, y))`. Я не ошибся, это не лишние скобочки, это [кортеж]() ([tuple](https://en.wikipedia.org/wiki/Tuple)) - неизменяемый, упорядоченный, разнородный список значений фиксированного размера. Как говорит официальная документация эта структура данных очень удобна, когда вам нужно передать или вернуть несколько значений _без лишних церемоний_. +Но особенность тут всё же есть - `reason±fn((x, y))`. Я не ошибся, это не лишние скобочки, это [кортеж](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D1%82%D0%B5%D0%B6_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)) ([tuple](https://en.wikipedia.org/wiki/Tuple)) - неизменяемый, упорядоченный, разнородный список значений фиксированного размера. Как говорит официальная документация эта структура данных очень удобна, когда вам нужно передать или вернуть несколько значений *без лишних церемоний*. Ну с неизменяемостью и фиксированным размером, я думаю, всё понятно. Упорядоченность элементов позволяет обойтись без имён свойств. А благодаря разнородности мы можем хранить значения разных типов в одном кортеже. -Т.е. _tuple_ может заменить нам объекты, но я бы не стал их использовать для сущности с более чем тремя атрибутами - сложно будет запомнить, что зачем идёт. А вот для хранения пары координат они подходят идеально 😉. +Т.е. *tuple* может заменить нам объекты, но я бы не стал их использовать для сущности с более чем тремя атрибутами - сложно будет запомнить, что зачем идёт. А вот для хранения пары координат они подходят идеально 😉. -В _js_, кстати, аналогом могут выступить массивы, для них (как и в _reason_ для _tuple_) есть очень удобный синтаксис деструктуризации: `js±let [x, y] = coordinates;` (в reason: `reason±let (x, y) = coordinates;`). +В *js*, кстати, аналогом могут выступить массивы, для них (как и в *reason* для *tuple*) есть очень удобный синтаксис деструктуризации: `js±let [x, y] = coordinates;` (в reason: `reason±let (x, y) = coordinates;`). Ну давайте наконец попробуем создать случайно заполненную вселенную: @@ -230,8 +230,8 @@ Js.log(universe); ## Итого -Не смотря на то, что с точки зрения игры мы продвинулись не так далеко, мы теперь знаем что такое _reason_, откуда он пришёл и где его можно использовать, а также разобрали несколько языковых конструкций. +Не смотря на то, что с точки зрения игры мы продвинулись не так далеко, мы теперь знаем что такое *reason*, откуда он пришёл и где его можно использовать, а также разобрали несколько языковых конструкций. -Надеюсь, вам было интересно, и вы ещё придёте читать другие статьи про _reason_ и не только. +Надеюсь, вам было интересно, и вы ещё придёте читать другие статьи про *reason* и не только. -А ещё не забывайте давать обратную связь. Сделать это можно через комментарии, а можно выделив текст на странице отправить сообщение о какой-либо неточности. +А ещё не забывайте давать обратную связь. Сделать это можно через комментарии, а можно выделив текст на странице отправить сообщение о какой-либо неточности. \ No newline at end of file