Как стать автором
Обновить

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

Время на прочтение5 мин
Количество просмотров7.8K

Если вы никогда не видели кода на gleam, то выглядит он примерно так. Пример взят из стандартной библиотеки. Стандартная библиотека, кстати, довольно полная, но ещё не совсем документирована. Поэтому, если захотите ей пользоваться, придётся немного смотреть в исходный код.

Синтаксис, наверно, ближе всего к Rust. В нём есть все прелести функционального программирования, например, отсутствует конструкция return.

Код разбит на модули и функции. Модули соответствуют файлам-исходникам с интуитивно понятным синтаксисом для импорта. Этим он отличается, скажем, от языка Elixir, где физические и логические модули - это не одно и тоже. Кроме этого, в эликсире модулям соответствуют структуры данных (struct), что вносит ещё больше путаницы.

Язык довольно минималистичный. Например, в нём отсутствуют for и if. Также отсутствует синтаксис для словарей, так что pattern matching для них тоже не сделать. Интересующиеся могут почитать The Gleam Book, чтобы узнать, что в gleam есть, а чего нет.

Также, в gleam отсутствуют "атомы". Erlang использует атомы как константы. Например, при успешном завершении функция может вернуть атом ok, а при не успешном - error. В gleam для этих целей используются типы. Напрямую атомы создавать в gleam нельзя, хотите использовать атом - создавайте новый тип. Решение оригинальное и мне очень нравится. Например, для упомянутого случая это выглядит так:

let parsed = case parse_int("123") {
  Error(e) -> io.println("That wasn't an Int")
  Ok(num) -> num
}

Кстати, об ошибках. В gleam нет конструкции try..catch (которая есть в эрланге). Вместо этого, рекомендуется использовать вышеуказанный подход, напоминающий Rust. Вообще, в эрланге существует принцип "let it crash", который не рекомендует ловить исключения, вместо этого, позволяя процессу, в котором исключение возникло, крэшнуться. В принципе, можно сказать, что gleam следует рекомендациям, потому что в нём не существует в принципе возможности поймать исключение.

Функции - приватные по дефолту. Чтобы сделать публичную функцию, нужно ей указать модификатор pub. Подойдёт для лиц, не стремящихся к публичности. Также есть простой и понятный способ для объявления констант на уровне модуля с помощью слова const.

Есть ещё интересная конструкция use, у которой, наверно, нет аналогов в других языках. Автор её описывает как синтаксический сахар для функций-обёрток. Только на стероидах. И приводит такой пример

pub fn main() {
  use <- logger.record_timing
  use db <- database.connect
  use f <- file.open("file.txt")
  // Do with something here...
}

Это эквивалентно следующему коду

pub fn main() {
  logger.record_timing(fn() {
    database.connect(fn(db) {
      file.open("file.txt", fn(f) {
        // Do with something here...
      })
    })
  })
}

В частности, это один из способов решить проблему callbacks hell.

Ну и, конечно, нужно отдельно сказать про типы. Они в gleam опциональные, их можно не объявлять. Можно аннотировать любое выражение типом. Кастомные типы объявляются так

type User {
  LoggedIn(name: String)
  Guest
}

Как видите, можно объявлять несколько типов внутри одного блока type. Но - можно и один. Использовать их потом можно так

fn get_name(user) {
  case user {
    LoggedIn(name) -> name
    Guest -> "Guest user"
  }
}

То есть, типы используются как при pattern-matching'е, так и для статического анализа. Есть также ограничения, которые gleam накладывает на структуры данных: в списках и словарях нельзя иметь значения разных типов. Компилятор запрещает.

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

Отдельно хочу отметить, что в gleam отсутствуют возможности для сумасшедшего мета-программирования, которые есть, например, в языке Elixir (макросы). Что меня очень радует: макросы для высокоуровневого языка - это однозначное зло. Хотя в системных языках выполнять код во время компиляции бывает нужно, и это совершенно нормально. Такие возможности есть, например, в языках Zig, Rust, C++.

gleam при сборке делает source-to-source преобразование своего кода в код на эрланге. Написан он на языке Rust.

Вот, это всё, что я вам хотел рассказать о языке gleam. В конце есть опрос. Придирчивый читатель может дальше не читать, потому что дальше будут предложения максимально не конкретные и максимально не соответствующие.

В общем, изучая возможности gleam, я поймал себя на мысли, что они целиком покрываются синтаксисом питона (я программист на питоне). Ну, или почти целиком: пайплайн-оператора разве что в питоне нет (этого: |>).

Так что, я подумал, что если какой-нибудь, не вполне адекватный, человек решит переделать gleam под питоновский синтаксис, то не встретит на этом пути никаких трудностей. Разумеется, такой человек стал бы использовать компилятор gleam для генерации кода на эрланге.

Возможно ли это, и насколько императивный язык отличается от функционального?

UPDATE: Часть, которая следует ниже, больше не актуальна. Тем не менее, я по-прежнему считаю, что отступы (или significant whitespace) сделали бы возможным более богатый синтаксис. gleam, в этом смысле, пытается сидеть на двух стульях: с одной стороны, у него нет разделителей между строками (;), с другой стороны, пробелы и переходы на новую строку не учитываются. Из-за этого многие синтаксические конструкции, увы, невозможны

В версии 3.10 в питон добавили match..case с довольно развесистым синтаксисом: с типами, условиями и всем, что только можно было добавить. Выглядит он так:

match user:
  case LoggedIn(name):
    # serve user
  case Guest():
    # serve guest

Годится ли он? Не совсем. Есть небольшая проблема: match должен быть выражением, должен возвращать значение. То есть, должно быть так

result = match user:
  # и дальше по тексту

То же самое касается других ключевых слов: if, for, try. Только в том случае, если блок match стоит последним в функции, годится его текущий синтаксис.

Про классы и весь ООП можно забыть (и нужно). Классы должны использоваться только для аннотации типов. В остальном же - ванильный питоновский синтаксис вполне самодостаточен. Можно брать парсер для питона и парсить им код.

Но не бывает так, чтобы у кого-то была возможность добавить ещё синтаксис, и он этого не сделал. Если это случится в нашем случае, то что это будет? Вот возможные варианты.

1) Pipeline operator (|>)

Уж очень хорошо в коде смотрится. Наиболее полезен в конце выражений match, if, for:

result = match obj:
  case {'data': data}: data
|> process_data()
|> process_more()

Также блок match может сам стоять после оператора пайплайна:

result = match obj:
  # что-то
|> match:
  # ещё что-то

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

2) Анонимные функции

В питоне есть лямды, но они неполноценные. Полноценные бы выглядели так:

f = def(x, y):
  x + y

Вообще-то, это то же самое, что объявить функцию обычным способом. Зато лямду можно использовать после оператора пайплайна:

result = match value:
  # что-то
|> def(x):
  # что-то на выходе

3) Pattern matching на операции присваивания

MyType(data) = obj

Это удобно - иметь такую возможность. Синтаксис - такой же, как в части case у match.

Таким образом, можно с уверенностью заключить, что gleam вполне мог бы иметь синтаксис питона. Надеюсь, я удовлетворил ваше любопытство в плане, как бы это могло быть, и убедил в том, что синтаксис у питона действительно богат.

Что вы скажете?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Опрос
43.96% gleam — интересный язык40
5.49% предложенный синтаксис чудесен5
53.85% без комментариев49
Проголосовал 91 пользователь. Воздержались 30 пользователей.
Теги:
Хабы:
Всего голосов 12: ↑9 и ↓3+12
Комментарии18

Публикации

Истории

Работа

Data Scientist
76 вакансий
Python разработчик
126 вакансий

Ближайшие события

AdIndex City Conference 2024
Дата26 июня
Время09:30
Место
Москва
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область