Разработка React-приложений с использованием ReasonReact

Автор оригинала: David Kopal
  • Перевод
Вы применяете React для создания пользовательских интерфейсов? Автор материала, перевод которого мы публикуем, говорит, что он тоже работает с React. Здесь он хочет рассказать о том, почему для написания React-приложений стоит использовать ReasonML.



React — это очень хороший инструмент для разработки интерфейсов. Можно ли сделать его ещё лучше? Для того чтобы улучшить работу с React, сначала надо понять его основные проблемы. В частности, проблему, у истоков которой лежит тот факт, что React — это JavaScript-библиотека.

React и JavaScript


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

Иммутабельность — это один из базовых принципов React. Мутации свойств компонентов или состояния приложения весьма нежелательны, так как подобное может привести к непредсказуемым последствиям. В JavaScript нет стандартных механизмов для обеспечения иммутабельности. Структуры данных делают иммутабельными либо придерживаясь неких соглашений, либо используя библиотеки наподобие immutable-js.

Библиотека React основана на принципах функционального программирования, так как React-приложения представляют собой композиции функций. Хотя в JavaScript имеются некоторые возможности функционального программирования, такие, как функции первого класса, функциональным языком программирования он не является. Если на JavaScript нужно писать хороший декларативный код, приходится прибегать к сторонним библиотекам вроде Lodash/fp или Ramda.

А что не так с системой типов? В React имеется концепция PropTypes. Её используют для имитации типов в JavaScript, так как этот язык, сам по себе, не является статически типизированным. Для того, чтобы пользоваться в JS выгодами статической типизации, опять же, приходится прибегать к сторонним инструментам, таким, как Flow и TypeScript.


Сравнение React и JavaScript

Как видите, JavaScript не совместим с базовыми принципами React.

Существует ли язык программирования, который лучше JavaScript согласуется с React?
На данный вопрос можно дать положительный ответ. Этот язык — ReasonML.

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


Сравнение React, JavaScript и Reason

Получается, что Reason совместим с базовыми принципами React.

Reason


Reason — это не новый язык. Он представляет собой альтернативный, напоминающий JavaScript, синтаксис и набор инструментов для OCaml — функционального языка программирования, который существует уже более 20 лет. Reason был создан разработчиками из Facebook, которые уже использовали OCaml в своих проектах (Flow, Infer).


OCaml

С-подобный синтаксис Reason делает OCaml доступным для программистов, которые знакомы с такими распространёнными языками, как JavaScript или Java. Reason даёт разработчику более качественную, в сравнении с OCaml, документацию, вокруг него сложилось постоянно растущее сообщество энтузиастов. Кроме того, то, что написано на Reason, несложно интегрировать с существующими JS-проектами.


Reason

Основой Reason является OCaml. Reason имеет ту же семантику, что и OCaml, различается лишь синтаксис. Это означает, что Reason даёт возможность писать OCaml-код, используя JavaScript-подобный синтаксис. В результате в распоряжении программиста оказываются такие замечательные возможности OCaml, как строгая система типов и механизм сопоставления с образцом (pattern matching).

Взглянем на фрагмент Reason-кода для того, чтобы ознакомиться с его синтаксисом.

let fizzbuzz = (i) =>
  switch (i mod 3, i mod 5) {
  | (0, 0) => "FizzBuzz"
  | (0, _) => "Fizz"
  | (_, 0) => "Buzz"
  | _ => string_of_int(i)
  };
for (i in 1 to 100) {
  Js.log(fizzbuzz(i))
};

Хотя в этом фрагменте используется механизм сопоставления с образцом, он остаётся весьма похожим на JavaScript.

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

BuckleScript


Одной из интереснейших возможностей Reason можно назвать компилятор BuckleScript, который берёт код, написанный на Reason и преобразует его в читабельный и производительный JS-код, кроме того, неплохо очищая его от неиспользуемых конструкций.


BuckleScript

Читабельность результатов работы BuckleScript придётся кстати в том случае, если вы работаете в команде, в которой не все знакомы с Reason. Эти люди, по крайней мере, смогут читать результирующий JS-код.

Код на Reason иногда так похож на JS-код, что компилятору вовсе не нужно его преобразовывать. Благодаря такому положению дел можно наслаждаться благами статической типизации Reason и писать код, выглядящий так, будто он написан на JavaScript.

Вот пример кода, который будет работать и в Reason и в JavaScript:

let add = (a, b) => a + b;
add(6, 9);

BuckleScript поставляется с четырьмя библиотеками. Это — стандартная библиотека, называемая Belt (стандартной библиотеки OCaml тут недостаточно), и привязки для JavaScript, Node.js и для API DOM.

Так как BuckleScript основан на компиляторе OCaml, компиляция оказывается очень быстрой — гораздо быстрее чем у Babel и в несколько раз быстрее чем у TypeScript.

Скомпилируем с помощью BuckleScript вышеприведённый фрагмент Reason-кода, содержащий функцию fizzbuzz(), в JavaScript.


Компиляция Reason-кода в JavaScript с помощью BuckleScript

Как видите, JS-код оказался вполне читаемым. Выглядит он так, как будто написан человеком.

Программы, написанные на Reason, компилируются не только в JavaScript, но и в нативный код, и в байт-код. В результате, например, можно написать приложение на Reason и запустить его в браузере, на MacOS, на смартфонах, работающих под управлением Android и iOS. Существует игра Gravitron, написанная Джаредом Форсайтом на Reason. Её можно запускать на всех вышеупомянутых платформах.

Организация взаимодействия с JavaScript


BuckleScript даёт возможность организации взаимодействия Reason и JavaScript. Это означает не только возможность использования рабочего JS-кода в кодовой базе Reason, но и возможность взаимодействия кода, написанного на Reason, с этим JavaScript-кодом. Как результате, код, написанный на Reason, легко поддаётся интеграции в существующие JS-проекты. Более того, в Reason-коде можно использовать JavaScript-пакеты из NPM. Например, можно создать проект, в котором совместно используются Flow, TypeScript и Reason.

Однако всё не так уж и просто. Для того чтобы использовать JavaScript-код или библиотеки в Reason, их сначала надо портировать с использованием привязок (биндингов) Reason. Другими словами, нам, для того, чтобы воспользоваться строгой системой типов Reason, нужны типы для обычного JavaScript-кода.

Если вам нужно воспользоваться какой-нибудь JavaScript-библиотекой в Reason-коде, сначала стоит обратиться к Reason Package Index (Redex) и узнать, была ли эта библиотека уже портирована в Reason. Проект Redex представляет собой каталог библиотек и инструментов, написанных на Reason и JavaScript-библиотек с Reason-привязками. Если вам удалось найти в этом каталоге нужную библиотеку, её можно установить как зависимость и использовать в Reason-приложении.

Если же найти нужную библиотеку не удалось, вам придётся самостоятельно написать биндинги. Если вы только начинаете знакомство с Reason, учтите, что писать биндинги — это задача не для новичка. Это — одна из самых сложных задач, из тех, что приходится решать тем, кто программирует на Reason. На самом деле, это — тема для отдельной статьи.

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

ReasonReact


В начале материала мы говорили о том, что он посвящён разработке React-приложений с использованием Reason. Заниматься этим можно благодаря библиотеке ReasonReact.

Возможно, сейчас вы думаете: «Мне всё ещё непонятно — почему надо писать React-приложения на Reason». Однако мы уже обсудили основную причину использования связки React и Reason, которая заключается в том, что React лучше совместим с Reason чем с JavaScript. Почему это так? Всё дело в том, что React был создан в расчёте на Reason, или, точнее, в расчёте на OCaml.

Путь к ReasonReact



Первый прототип React был разработан Facebook и был написан на Standard Meta Language (StandardML), на языке, который является родственником OCaml. Затем React перевели на OCaml, кроме того, React перенесли на JavaScript. Сделано это было из-за того, что весь веб использовал JavaScript и, вероятно, неразумным было бы делать заявления вроде: «А теперь мы будем писать UI на OCaml». Перевод React на JavaScript себя оправдал и привёл к широкому распространению этой библиотеки.

Как результат, все привыкли воспринимать React в виде JS-библиотеки. React, а также другие библиотеки и языки, такие как Elm, Redux, Recompose, Ramda, и PureScript, способствовали популяризации функционального стиля программирования в JavaScript. А благодаря распространению Flow и TypeScript в JavaScript стала популярна и статическая типизация. В итоге парадигма функционального программирования с использованием статических типов стала главенствующей в мире разработки фронтенда.

В 2006 году компания Bloomberg создала и перевела в разряд опенсорсных проектов компилятор BuckleScript, который преобразует OCaml в JavaScript. Это позволило им писать более качественный и безопасный фронтенд-код, используя строгую систему типов OCaml. Они взяли оптимизированный и очень быстрый компилятор OCaml и заставили его генерировать код на JavaScript.

Популярность функционального программирования и выпуск BuckleScript создали идеальный климат, который позволил Facebook вернуться к исходной идее React — библиотеки, которая изначальна была написана на StandardML.


ReasonReact

Они смешали семантику OCaml с синтаксисом JavaScript и создали Reason. Кроме того, они создали Reason-обёртку для React, представленную в виде библиотеки ReasonReact, которая обладает дополнительными функциями, такими, как инкапсуляция принципов Redux в компонентах с состоянием. Сделав это, они вернули React к его истокам.

О возможностях React в Reason


Когда библиотеку React переводили на JavaScript, возможности языка подгоняли под нужды React за счёт создания различных библиотек и инструментов. Подобный подход, в частности, означает необходимость в большом числе зависимостей для проектов. Уж не будем говорить о том, что подобные библиотеки постоянно развиваются, и в них регулярно происходят изменения, делающие их новые версии несовместимыми со старыми. Как результат, разработчику приходится очень серьёзно и осторожно относиться к обслуживанию библиотек, от которых зависят его проекты.

Это добавляет дополнительный уровень сложности в JavaScript-разработку. Например, типичное React-приложение обычно содержит, как минимум, зависимости, которые можно видеть на следующем рисунке.


Зависимости типичного React-приложения

Вот какие задачи решают эти зависимости:

  • Статическая типизация — Flow/TypeScript.
  • Иммутабельность — ImmutableJS.
  • Маршрутизация — ReactRouter.
  • Форматирование кода — Prettier.
  • Линтинг — ESLint.
  • Вспомогательные функции — Ramda/Lodash.

Теперь воспользуемся, вместо React для JavaScript, библиотекой ReasonReact. Нужны ли нам, при таком подходе, все эти зависимости?


Переход на ReasonReact

Проанализировав тот же список задач, которые раньше решались с помощью дополнительных средств, мы выясним, что все их можно решить с помощью встроенных средств ReasonReact. Подробности о них вы можете почитать здесь.

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

Всё это оказывается возможным благодаря использованию языка OCaml, которому уже более 20 лет. Это зрелый язык, базовые принципы и механизмы которого проверены временем и стабильны.

Что дальше?



Если вы родом из мира JavaScript, вам будет несложно начать работу с Reason благодаря тому, что синтаксис этого языка похож на JavaScript. Если вы раньше писали React-приложения, перейти на Reason вам будет ещё легче, так как вы можете, при работе с ReasonReact использовать все свои познания в области React. В основе ReasonReact лежит та же модель мышления, что и в основе React, процесс работы с ними так же очень схож. Это означает, что при переходе на Reason вам не придётся начинать с нуля. Вы разберётесь с Reason в процессе работы.

Лучший способ начать использовать Reason в своих проектах заключается в том, чтобы постепенно вводить в них фрагменты, написанные на Reason. Как уже было сказано, Reason-код можно использовать в JS-проектах, равно как и JS-код в Reason-проектах. Этот подход применим и при использовании ReasonReact. Можно взять ReasonReact-компонент и использовать его в традиционном React-приложении, написанном на JavaScript.

Именно такой вот инкрементальный подход был выбран разработчиками Facebook, которые широко использовали Reason при разработке мессенджера Facebook.

Если вы хотите написать React-приложение с использованием Reason и на практике изучить основы этого языка, взгляните на этот материал, где пошагово разбирается разработка игры «Крестики-нолики».

Итоги


У создателей Reason было два варианта действий. Первый заключался в том, чтобы взять JavaScript и как-то его улучшить. Если бы они избрали этот путь — им пришлось бы иметь дело с историческими недостатками JS.

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

React тоже основан на принципах OCaml. Именно поэтому писать React-приложения гораздо легче и приятнее с использованием Reason. Работа с React в Reason предлагает более стабильный и безопасный подход к созданию React-компонентов, так как строгая система типов страхует разработчика и ему не приходится сталкиваться с большинством исторических проблем JavaScript.

Уважаемые читатели! А вы пробовали ReasonReact?

RUVDS.com
928,00
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

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

    0
    Как то скептически отношусь ко всяким «компилируемым в JS» языкам. Мало того, что повышает порог вхождения, так ещё и приносит новые проблемы, в данном случае «биндинги не для новичков», зачем? Если есть immutable.js, где нет таких проблем
      +2
      Библиотека React основана на принципах функционального программирования, так как React-приложения представляют собой композиции функций. Хотя в JavaScript имеются некоторые возможности функционального программирования, такие, как функции первого класса, функциональным языком программирования он не является

      И далее вывод из этого и других посылов:
      Как видите, JavaScript не совместим с базовыми принципами React.

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

      Тоже не соответствует действительности, по крайней мере, частично. В JavaScript «из коробки» иммутабельны строки. Для объектов есть Object.freeze() и Object.defineProperty() с тонкой настройкой каждого свойства на чтение/запись/модификацию.
      В статье также не упомянуты принципы React, которые очень хорошо сочетаются с Javascript. Например, в своей простейшей форме компонент Реакта – это Javascript-функция. JSX синтаксис неплохо вписывается в Javascript-инфраструктуру, потому что JavaScript-выражения можно использовать в любом месте JSX. Принцип событий и их обработки — это вообще одна из основных причин, для чего Javascript создавался как язык программирования в вебе и основа изменения компонентов в React.
        –2
        Зело страшный физбаз приведен для сравнения с несравненным RE (чем бы оно ни было).
        Вот такой еще есть:
        const f = 'Fizz', b = 'Buzz', fb = f+b;
        const pattern = [,,f,,b,f,,,f,b,,f,,,fb];
        for(let num = 1; num <= 100; )
        for(let i = 0; i < 15 && num <= 100; i++, num++) 
        	console.log( pattern[i] ? pattern[i] : num );
        
          +2
          В статье приведен скопилированный js из ReasonML
            0
            Ах, чёрт! И верно… Подайте пепел.
          –2
          В JavaScript нет стандартных механизмов для обеспечения иммутабельности

          Спред-оператор, slice, map/filter/reduce, Object.assign вполне себе обеспечивают иммутабельность
            +3
            Это значит что они запрещают менять объект? Или надо программисту памятку повесить, что пользоваться можно толкьо этими ф-циями?
              –1
              Это значит, что с их помощью можно спокойно работать с объектами, не изменяя первоначальные.
              И как мне кажется, программист в состоянии запомнить, что нужно сделать для достижение той или иной цели. Вы же не пишете памятки о том, что для объявления константы нужно использовать ключевое слово const. Или пишете?
                +1

                Вы не поверите, но можно "спокойно" (а зачем нервничать) работать с объектами, не изменяя их, даже без перечисленных вами функций и spread-operator-а. Причём, я полагаю, в любом языке программирования. Речь всё же о другом ;)

                  –5
                  Вы же не пишете памятки о том, что для объявления константы нужно использовать ключевое слово const. Или пишете?
                  \

                  const не нужна. (С)
                    –2
                    В результате, из-за того, что использование const может привести к путанице, и из-за того, что при наличии ключевого слова let наличие const выглядит избыточным, я решил всегда использовать let.


                    const не нужна вообще! (С)
                      0

                      Переходите на php, там не нужны ни var, ни const, ни let ;)

                        –1
                        там не нужны ни var, ни const, ни let ;)

                        Дело не в количестве, дело в том, что const в Javascript имеет тот же самый смысл что final в Java.
                        Но final в Java программисты так редко практически вообще используют (хотя могут писать final хоть в каждой строчке почти), что было бы странно чтобы программисты Javascript вдруг, с какого-то бодуна, стали массово и всюду использовать const.

                        Практика показала, что в реальном программировании (без «финишной лакировки» кода) использование и final и const не имеет никакого смысла.

                        const практически не нужен никому вообще! (С)

                        P.S. О лени — Мне один из разработчиков на JavaScript сообщил, что вначале они повелись и писали const («метим всё const, а затем уже у тех переменным, значение которых меняем при разработке(написании кода), метим let.») — «полируя код». Но со временем перестали, так как при рефакторинге им приходилось возвращаться назад по коду и заменять const на let, и они стали использовать только и только let.
                          +1

                          Да-да-да, я читал всё, что вы там писали. И я в корне не согласен почти со всем. Но не вижу смысла спорить об этом. Ничего нового я вам не открою, в том топике были даны все ответы.


                          P.S. И да, ваша практика в корне противоречит моей. Думаю на этом можно вопрос закрыть :) Пусть это будет делом вкуса.

                            –1
                            ваша практика в корне противоречит моей.
                            А причина?

                            Java-программисты только под дулом пистолета будут писать final String myDarling =…

                            JavaScript-программисты напишут let myDarling =…

                            Почему?

                            JavaScript-программистам надо что-то писать при объявлении переменной. Выбор есть — var, let, const или ничего.

                            Но ничего писать нельзя! Ибо глобальная видимость.
                            Остаётся выбор из var, let, const.

                            У Java-программисты есть выбор писать:
                            final String myDarling =…
                            или
                            String myDarling =…

                            Конечно они выбирают писать без final

                            Другое дело, если бы да кабы в JavaScript было:
                            var — это как и сейчас var
                            const — это как и сейчас const
                            global — это как сейчас ничего
                            ничего — это как и сейчас let

                            В этом случае JavaScript-программисты на практике почти никогда бы вообще не указывали этот модификатор видимости переменной при её объявлении. Но… поезд ушёл.

                              +2

                              Я там выше про PHP не спроста упомянул. Убрав keyword для декларации переменной вы получаете хаос и жуткие лямбды с use($var1, $var2).

                                –1
                                Убрав keyword для декларации переменной вы получаете хаос и жуткие лямбды с use($var1, $var2).
                                Вы не поняли. Убирать уже… поздно.

                                JavaScript был разработан не для программистов, а для обычных юзеров, коим удобнее было (то есть они часто это использовали) никак не декларировать переменную.
                                Но, JavaScript стал использоваться программистами, которым удобно сейчас (то есть они наиболее часто это используют) декларировать переменную имеенно как let.

                                P.S.
                                Языки редко используют те, для кого они предназначались:

                                С — для разработчиков операционной системы, что и сейчас есть.
                                С++ — как универсальный язык, но который занял нишу у разработчиков игрушек и видео-звуко-фото-редакторов.
                                Паскаль — для обучения программированию.
                                Фортран для физиков.
                                PL/1 — универсальный язык для всех — канул в лету.
                                Cobol — язык для экономистов — написано много было, всё переводят на Java его.
                                Java — язык для динамических картинок в броузере — но залез аж на сервер и там глубоко обосновался.
                                SQL — язык был сознательно выбран и разработан для бухгалтеров, кладовщиков, экономистов и библиотекарей — но его выхватили себе программисты баз данных и не выпускают из рук, лет 15 назад не пустив в него ООП.

                                Неисповедимы пути языков. (С)

                                  0
                                  Java — язык для динамических картинок в броузере — но залез аж на сервер и там глубоко обосновался

                                  Цитата года ;)

                  0

                  Технически, для обеспечения иммутабельности, можно использовать Object#freeze. Хотя конечно это все равно не будет работать с вложенными объктами по-человечески.

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

                И какой процент этого требуется в Web-приложениях?

                Нет, спасибо, не надо тут чистую функциональщину.
                  +2
                  Я надеялся, прочитав эту статью увидеть пример кода react-приложения на Reason, чтобы увидеть его преимущества или недостатки. Вместо этого я увидел очередные разглагольствования евангелиста и fizzbuzz, который кроме паттерн-матчинга ничего не продемонстрировал :( Ни про вывод типов толком не рассказали, ни про мутабельность/иммутабельность, вообще по факту ничего. А если это действительно OCaml для разработки фронтенда, то с этого стоило начинать, потому что OCaml вообще-то не чистый функциональный язык и эти моменты НЕ очевидны.
                  В общем не надо так, переводите лучше технические статьи.

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

                  Самое читаемое