REPL

Начать разбираться с основными конструкциями языка Elm можно через его REPL. Для этого необходимо предварительно установить Elm. На официальном сайте языка присутствует руководство для разных операционных систем, а также по конфигурированию популярных редакторов кода. Для macOS установка сводится к одной команде: brew install elm, если вы используете Homebrew.

Для того, чтобы запустить REPL введите elm repl в вашем любимом терминале. В командной строке вы увидите подобный вывод:

$ elm repl
---- Elm 0.19.0 ----------------------------------------------------------------
Read <https://elm-lang.org/0.19.0/repl> to learn more: exit, help, imports, etc.
--------------------------------------------------------------------------------
>

Выйти из REPL можно сочетанием клавиш Ctrl + D.

Выражения (expressions)

По традиции попробуем написать Hello World программу. В REPL введем следующее:

> "Hello, World"

В ответ получаем:

"Hello, World" : String

Вы можете заметить, что правая часть результата является аннотацией типа – в нашем случае строки.

Давайте взглянем еще на несколько примеров:

> "Hello, World"
"Hello, World" : String

> "Hello, " ++ "World"
"Hello, World" : String

> "Pi is " ++ String.fromFloat pi
"Pi is 3.141592653589793" : String

В отличие от JavaScript, для конкатенации (объединения) строк в Elm необходимо применять оператор ++. А что насчет оператора + и других арифметических операций:

> 1 + 2
3 : number

> 10 - (12 * 10)
-110 : number

> 2 ^ 10
1024 : number

> 29 / 10
2.9 : Float

> 29 // 10
2 : Int

> 5 % 2
-- UNKNOWN OPERATOR -------------------------------------------------------- elm

Elm does not use (%) as the remainder operator:

4|   5 % 2
       ^
If you want the behavior of (%) like in JavaScript, switch to:
<https://package.elm-lang.org/packages/elm/core/latest/Basics#remainderBy>

If you want modular arithmetic like in math, switch to:
<https://package.elm-lang.org/packages/elm/core/latest/Basics#modBy>

The difference is how things work when negative numbers are involved.
    
> remainderBy -5 2
2 : Int

> modBy -5 2
-3 : Int

Таким образом:

  • конкатенация (объединение): оператор ++, использование его на числах вызовет ошибку;
  • сложение: оператор + может быть использован только с числами;
  • вычитание, умножение, деление и деление без остатка: -, *, /, //;
  • в версиях Elm < 0.19 для получения остатка от деления использовался такой же как и в JavaScript оператор %, в 0.19 он был заменен на функции remainderBy и modBy .

Пытаясь использовать отсутствующий оператор %, мы в воочию познакомились с дружественными и предельно ясными ошибками компилятора языка Elm. Только взгляните на вывод выше! Прочитав сообщение об ошибке мы точно знаем что сделали не так и что требуется сделать для того, чтобы исправить проблему.

Строки и символы

Elm разделяет строки и индивидуальные UTF-8 символы, из которых они состоят. Двойные кавычки отведены под строковые литералы, точно так же как в JavaScript, а одинарные представляют символьные литералы.

> "a"
"a" : String

> 'a'
'a' : Char

> "abc"
"abc" : String

> 'abc'
-- PARSE ERROR ------------------------------------------------------------- elm

Ran into a bad use of single quotes.

4|   'abc'
     ^^^^^
If you want to create a string, switch to double quotes:

    'this' => "this"

Hint: Unlike JavaScript, Elm distinguishes between strings like "hello" and
individual characters like 'A' and '3'. If you really do want a character
though, something went wrong and I did not find the closing single quote.

> ""
"" : String

> ''
-- PARSE ERROR ------------------------------------------------------------- elm

Ran into a bad use of single quotes.

4|   ''
     ^^
If you want to create a string, switch to double quotes:

    'this' => "this"

Hint: Unlike JavaScript, Elm distinguishes between strings like "hello" and
individual characters like 'A' and '3'. If you really do want a character
though, something went wrong and I did not find the closing single quote.

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

Комментарии

Можно задавать двумя способами аналогичными // и /*, */ в JavaScript:

  • Для однострочных комментариев в Elm используется --;
  • Для многострочных – {- в начале комментируемого текста и -} в конце.

Присвоение имен значениям

Присвоение имен значениям происходит привычным для многих разработчиков образом:

> hours = 24
24 : number

> hours
24 : number

Однако, после того, как имя было присвоено значению, переназначить его другому значению в текущем скоупе уже нельзя. Подобное поведение можно сравнить с conts в JavaScript, хотя концепция иммутабельности – это совсем не то же самое, что и константы.

Присваивая имена значениям, помните о следующих правилах:

  • имя должно начинаться со строчной буквы, после которой может следовать последовательность из букв, цифр и символов нижнего подчеркивания _;
  • согласно соглашению языка Elm, все буквы должны быть в одной непрерывной последовательности; например, top5 – допустимое имя, а top5products нет, т.к. последовательность символов прервана цифрой 5;
  • исходя из перечисленных выше правил, не допускается использование змеиного регистра hello_world или HELLO_CRAZY_WORLD для имен значений; используйте camelCase;
  • использование имен, не соответствующих соглашению Elm, возможно и компилятор не выдаст сообщение об ошибке, но лучше все же придерживаться принятых норм и экспериментировать с именами только в REPL.

Присвоение имен выражениям

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

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

  • "Hello, " ++ "World" -> "Hello, World";
  • 2 ^ 10 -> 1024;
  • pi -> 3.141592653589793;
  • "a" -> "a".

Так как выражения – это все что выполняется в значение, то значения "Hello, World", "a" тоже выражение, которые уже выполнены.

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

Рассмотрим следующий пример:

balance = (amount > 0) ? "positive" : "negative"

balance = if (amount > 0) { "positive" } else { "negative" }

Первая строка кода – тернарное выражение, результатом выполнения которого будет значение, присвоенное переменной balance.

Вторая строка кода – условный оператор, и так как оператор не выполняется в значение, то при попытке присвоить результат выполнения переменной balance, мы получим синтаксическую ошибку.

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

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

Булевы типы и условия

Булевых типов в Elm всего два True и False. Работа с ними аналогична работе с булевыми типами в JavaScript. Но есть некоторые различия:

  • вы пишите True и False вместо true и false;
  • вы пишите /= вместо !===;
  • для отрицания вы используете функцию not вместо префикса ! в JavaScript.
> pi == pi
-- PARSE ERROR ------------------------------------------------------------- elm

Something went wrong while parsing pi's definition.

4| pi == pi
       ^
I was expecting to see an expression, like x or 42.

> (pi == pi)
True : Bool

> pi /= pi
False : Bool

> not (pi == pi)
False : Bool

> pi <= 0 || pi >= 10
False : Bool

> 3 < pi && pi < 4
True : Bool

Теперь давайте представим, что вы хотите вывести на экран строку отображающую количество заказов в вашем аккаунте. Если заказ всего один, то необходимо отрендерить 1 order, если больше, то n orders. Типичная pluralize функциональность, которая есть во многих библиотеках. На Elm базовую версию можно записать так:

ordersLabel = if ordersCount == 1 then "order" else "orders"

Каждый из компонентов выражения (условие, ветка истинности и ветка ложности) должен быть, как мы уже выяснили, также выражением, и результатом выполнения всего кода if-выражения будет одно единственное значение. Компилятор выдаст ошибку, если любой из трех компонентов if-выражения будет пропущен. Поэтому будьте внимательны и не пропускайте ветку else – это довольно частый случай для начинающих в Elm.

В Elm нет такого понятия, как истинность. Условия могут быть либо True, либо False. Другого не дано.

Расширим наш пример с заказами и добавим случай, когда количество заказов равно нулю – выведем на экран "no orders". В JavaScript для этого обычно используется условный оператор else if. Также как и в Elm:

-- if-expression inside else
if ordersCount == 1 then
  "order"
else
  (if ordersCount > 0 then "orders" else "no orders")

-- better formatiing
if ordersCount == 1 then
  "order"
else (if ordersCount > 0 then
  "orders"
else
  "no orders")

-- we can drop the parentheses
if ordersCount == 1 then
  "order"
else if ordersCount > 0 then
  "orders"
else
  "no orders"

Для того, чтобы выполнить в REPL мультистроковое выражение, в конце каждой строки необходимо добавить символ /.

Чуть позже в дополнение к if-else мы разберем еще один мощный механизм описания условий – case-выражение, аналога которому нет в JavaScript.

📖 Читать дальше: Функции | Выбор языка для проекта | 📚 Назад к оглавлению