На этих выходных я написал статью о том, что хочу сделать новый язык для BEAM со статической типизацией. И получил объёмный фидбэк - большая часть его содержалась, как обычно, между строк. Теперь хочу вам представить издание 2е, полностью переработанное.
Новый план состоит в том, чтобы использовать синтаксис Elixir как он есть (да!) Ну, разве что, типы в него добавить. Также, нужно сделать соответствие модулей и физических файлов 1:1.
UPDATE: berry будет поддерживать макросы, но только импортируемые.use
поддерживаться не будет.
import Ecto, macros: [from]
Таким образом, фанатам Ecto беспокоиться не о чём. Вы хотите спросить, не хочу ли я поделиться этой идеей с более широким сообществом? Я уже сделал это! А вот, какая была реакция - об этом читайте под катом.
* * *
Думаю, первый вопрос, который у вас возник, когда вы прочитали предисловие - это можно ли в Elixir добавить типы. Если вы читали предыдущую статью, то должны догадаться, что - можно:
def my_fun(x: Integer, m: Map) -> Bool do
end
Если вы используете внутри функции паттерн-матчинг, аннотации можно писать за скобками:
def my_fun([head | tail]) when head: Integer -> Bool do
Обычное использование guards - через условия в скобках после типа:
def my_fun(s: String(starts="-"), m: Map(size>0)) -> Nil do
Использование в case:
case m do
%{x: x} -> ...
Map(size>0) -> ...
end
В эрланге (и эликсире) нельзя получить тип текущего значения обычными способами, поэтому, если там стоит тип - это значит, его там явно указали.
Важная деталь: весь текущий синтаксис эликсира остаётся валидным в berry: секцию when можно писать и в обычном стиле тоже:
def my_fun([head | tail]) when is_integer(head) do
То есть, berry больше похож на следующую версию эликсира, чем на новый язык.
В целом, добавить типы в эликсир - вполне осуществимо. Рассудив таким образом и не утруждая себя обдумыванием деталей, я решил сразу поделиться идеей с сообществом, написав на форум текст такого содержания:
Для тех, кому лень читать мелкий шрифт, пересказываю содержание: "Всем известно, что в эликсире два минуса - это макросы и отсутствие типов. Народная мудрость гласит, что минус на минус даёт плюс. В связи с этим, почему бы не сделать версию Elixir 2.0, в которой одновременно добавить типы и убрать возможность писать макросы. Учитывая хайп, который вызовет добавление статической типизации, все очень быстро мигрируют на Elixir 2.0". В конце добавил успокоительную фразу о том, что не обязательно выпиливать макросы целиком, часть можно оставить - наиболее нужные.
Я думаю, читатели догадываются, что, в ответ на это предложение, я получил много ярких реплик. Вот одна из них :
"Теперь я вижу, что это первоапрельская шутка, хоть она и не подходит к сезону" (к сезону-то как раз подходит!). В общем, модераторы спохватились, только когда в топике уже было под 30 сообщений, так что это успех. Я не ожидал даже, что он первичную модерацию пройдёт. Основная мысль выступающих была, что макросы - это одна из важнейших фич языка, и что выпиливать её никто не станет даже под дулом пистолета.
Таким образом, напротив пункта "общение с сообществом" можно было смело ставить галочку. Но вернёмся к плану по внедрению berry-lang.
Раз уж начали говорить про макросы - у меня есть очень простая идея, как с ними быть. В berry будет разрешено использовать макросы, написанные на Elixir. При этом, писать макросы на berry будет нельзя: в конце концов, эликсир никуда не девается, хотите - пишите свой макрос на нём и используйте его библиотеку как зависимость. Таким образом, мы получаем возможность использовать Ecto, Phoenix и другие библиотеки, столь богатые на ключевые слова. Даже слово "use" можно разрешить для таких случаев - нам не жалко.
UPDATE: планы изменились. berry будет поддерживать макросы, но только импортируемые. В том числе, написание таковых.
import Ecto, macros: [from]
use
поддерживаться не будет. Это означает отсутствие поддержки Phoenix и ему подобных. Они с типами всё равно работать не могут, поскольку о типах не знают. А если не использовать типы, почему не писать на эликсире?
По поводу модулей: избавившись от макросов и от необходимости в конструкции "use", мы обнаружим, что совершенно не важно, в каком модуле находится функция. Поэтому естественно будет иметь прозрачную систему модулей и импортов. Где каждый модуль будет соответствовать файлу, где лежит его исходный код, и называться так же. Конструкция "defmodule" будет не нужна, а всё что было под этой конструкцией, попадёт в модуль berry.
Кстати, какое расширение выбрать для модулей, *.be?
Для импортов можно взять синтаксис эликсира, несмотря на то, что он довольно неуклюжий:
import my.mod, only: [my_func: 2]
Правда, есть проблема: import в эликсире используется только для функций и макросов, а для модулей нужно использовать require и alias. Конечно, 3 ключевых слова для импорта - это безобразие: нужно оставить только import, а остальные два сделать deprecated. И научить import импортировать модули. Кстати, импортировать также нужно будет и типы: они ведь тоже появятся. Можно сделать так:
import pak.pak2.mod,
functions: [my_fun],
macros: [my_macro],
types: [MyData]
Итоговый список изменений в Elixir:
добавляем типы
1 модуль = 1 файл, делаем единый способ импорта чего угодно по имени
запрещаем писать макросы
Если ещё резюмировать, berry - это диалект эликсира, который совместим с ним не только по функциям, но и по макросам. При этом, в нём самом макросы забанены.
Что скажет читатель? Должно полететь?