В предыдущей статье я приводил следующий пример:

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

Для того, чтобы перейти от частного случая с orders к более общему pluralize, нам необходимо оформить этот код в виде функции, которую мы сможем вызывать и не дублировать логику.

Функции в Elm представляют собой повторно-используемую логику. Они не являются объектами и не хранят состояние. Все что они делают – принимают значения в качестве аргументов и возвращают значение.

Давайте объявим нашу первую функцию isPositive. Она будет принимать число и возвращать:

  • True если оно положительное;
  • False в остальных случаях.
> isPositive num = num > 0
<function> : number -> Bool
    
> isPositive 3
True : Bool
    
> isPositive -3
False : Bool
    
> isPositive (123 - 40)
True : Bool

На JavaScript это выглядело бы так:

function isPositive(num) {
  return num > 0;
}

isPositive(3);

isPositive(-3);

isPositive(123 - 40);

Как вы видите, в Elm:

  • параметры функции перечисляются перед знаком =;
  • тело функции не заключается в фигурные скобки { };
  • ключевое слово return отсутствует.

Функция isPositive записана в одну строку и представлена в виде единственного выражения, которое выполняется в единственное значение. Его Elm и использует в качестве возвращаемого. Таким образом, все функции в Elm возвращают значения!

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

JavaScript:

function capitalize(str) {
  if (!str) {
    return str;
  }
  
  return str[0].toUpperCase() + str.slice(1);
}

Используя тернарный оператор, capitalize можно записать в более компактном и привычном для Elm виде:

function capitalize(str) {
  return !str ? str : str[0].toUpperCase() + str.slice(1);
}

Теперь тело функции похоже на if-выражение в Elm. Любой код с ранним return можно переписать с помощью условий, пусть он и будет не таким компактным. И это может быть легким переходом от вашего legacy JavaScript кода к более поддерживаемому  и лаконичному Elm коду.

Вернемся к первоначальной цели и с помощью изученного реализуем функцию pluralize:

> pluralize singular plural count = \
|   if count == 1 then singular else plural
<function> : a -> a -> number -> a

> pluralize "order" "orders" 4
"orders" : String

> pluralize "order" "orders" 1
"order" : String
  • Для функций, тело которых занимает более одной строки, в REPL необходимо использовать \ в конце каждой;
  • Отступы в Elm допускаются только пробелами! Символы табуляции – синтаксическая ошибка в Elm;
  • Параметры функции перечисляются через пробел, а не запятую, как в JavaScript.

Если функции необходимо передать в качестве аргумента выражение, то оно должно заключаться в круглые скобки:

-- Результат выражения (round 0.45) передается в качестве аргумента функции pluralize
> pluralize "product" "products" (round 0.45)
"products" : String

Импортирование функций

До текущего момента мы использовали только базовые операторы и функции, которые написали сами. Однако, как и в большинстве языков, в Elm можно использовать функции из сторонних / внешних модулей.

Модуль в Elm – именовая коллекция функций и других значений.

Модуль String – один из модулей стандартной библиотеки Elm. Примеры вызова функций из модуля:

> String.toLower "No Runtime Exceptions"
"no runtime exceptions" : String

> String.toUpper "A delightful language for reliable webapps."
"A DELIGHTFUL LANGUAGE FOR RELIABLE WEBAPPS." : String

> String.fromFloat pi
"3.141592653589793" : String

> String.fromInt 24
"24" : String

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

// JavaScript
"hello".length
"hello".toUpperCase()
String.fromCharCode(123)

-- Elm
String.length "hello"
String.toUpper "hello"
String.fromChar 'h'

Подобный подход организации функций используется в Elm не только для строк, но и для всех других задач. Нужна функция для работы со списками? Смотрите модуль List. Отлаживаете код? Модуль Debug вам в помощь. Ну вы поняли...

Другие модули могут быть найдены в официальном репозитории пакетов Elm.

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

Фильтрация символов в строке

filter – еще одна полезная функция модуля String. С ее помощью можно отфильтровать нежелательные символы в строке, например, оставить только цифры в номере телефона. String.filter имеет два параметра:

  • функцию, которая возвращает True если символ нужно оставить;
  • строку, по которой нужно произвести фильтрацию.

Функции в Elm могут передаваться другим функциям так же, как и другие значения. String.filterфункция высшего порядка, т.е. функция, которая может принимать другие функции в качестве аргументов.

> isKeepable char = char /= '-'
<function> : Char -> Bool

> isKeepable 'a'
True : Bool

> isKeepable '-'
False : Bool

> isKeepable '1'
True : Bool

> String.filter isKeepable "+7-920-123-45-67"
"+79201234567" : String

Область видимости и let-выражения

...


📖 Читать дальше | Выражения | 📚  Назад к оглавлению