Сайт: https://nim-lang.org
Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.
— Официальный сайт Nim
Nim — это довольно быстрый язык, на котором довольно быстро писать. Он весьма приятен для скриптов, имеет довольно большую экосистему, хорошую поддержку разных платформ. Это не самый простой язык: в нём много фич, и много способов сделать одно и то же, однако базовой продуктивности можно достичь за небольшое время.
iterator oddNumbers[Idx, T](a: array[Idx, T]): T =
for x in a:
if x mod 2 == 1:
yield x
for odd in oddNumbers([3, 6, 9, 12, 15, 18]):
echo odd
-
Nim — строго, статически типизированный язык. Поддерживает полиморфизм с помощью наследования. Система типов поддерживает дженерики. Есть ADT, реализованный как поддержка
case-of
оператора внутри определения типа:type NodeKind = enum # the different node types nkInt, # a leaf with an integer value nkFloat, # a leaf with a float value nkString, # a leaf with a string value nkAdd, # an addition nkSub, # a subtraction nkIf # an if statement Node = ref object case kind: NodeKind # the ``kind`` field is the discriminator of nkInt: intVal: int of nkFloat: floatVal: float of nkString: strVal: string of nkAdd, nkSub: leftOp, rightOp: Node of nkIf: condition, thenPart, elsePart: Node var n = Node(kind: nkFloat, floatVal: 1.0) # the following statement raises an `FieldDefect` exception, because # n.kind's value does not fit: n.strVal = ""
Интересен подход к методам: в Nim используется UCS, т. е. запись
obj.method(arg1, arg2)
полностью эквивалентна записиmethod(obj, arg1, arg2)
. -
Синтаксис Nim довольно компактный: например, допускается пропускать скобки при вызове функций. Когнитивный объём языка большой, есть много способов сделать одно и то же.
-
Nim транслируется в C, C++ или JS, что даёт ему неплохой перформанс и возможность использования на фронтенде.
-
Для «маргинального» языка у него довольно неплохая экосистема — в репозитории пакетов можно найти много разных библиотек. Во многом это объясняется хорошей интероперабельностью с C.
-
Обработка ошибок в Nim классическая, через исключения. Поддерживаются как checked exceptions (требующие в явном виде объявлять список исключений, которые может бросить функция), так и unchecked.
-
Кроме исключений, Nim может отслеживать любые другие эффекты — можно пометить функцию, как имеющую некий эффект, и компилятор не даст вызвать её из функции, которая этого эффекта не имеет:
type IO = object ## input/output effect proc readLine(): string {.tags: [IO].} = discard proc no_IO_please() {.tags: [].} = # the compiler prevents this: let x = readLine()
-
В частности, прагма
{. noSideEffect .}
(или ключевое словоfunc
) позволяет объявлять в Nim чистые функции. -
Модель управления памятью — на выбор. Самая новая и продвинутая из доступных это ORC: быстрый GC на основе счётчика ссылок с детектором циклов. Можно выбрать и другой GC, либо отключить его полностью.
Подробнее про ORC: https://nim-lang.org/blog/2020/10/15/introduction-to-arc-orc-in-nim.html
-
Есть очень мощная система макросов, которая позволяет определять свои синтаксические элементы. В частности, стрелочный синтаксис для анонимных функций определён как макрос в стандартной библиотеке, а не как встроенный синтаксический элемент: https://nim-lang.org/docs/sugar.html#%3D%3E.m%2Cuntyped%2Cuntyped
В целом, внутри макроса можно выполнять практически любой Nim-код, произвольным образом изменяющий AST.
Сайт: https://github.com/Hirrolot/metalang99
Metalang99 is a functional language aimed at full-blown C99 preprocessor metaprogramming. It features a wide range of concepts, including algebraic data types, control flow operators, collections, recursion, and auto-currying -- to develop both small and complex metaprograms painlessly.
— Репозиторий metalang99 на GitHub
metalang99 — это язык, написанный целиком на языке препроцессора C99. Он выполняется во время компиляции программы на C. Так как препроцессор C не является Тьюринг-полным, metalang99 также не Тьюринг-полный (в нём есть ограничение глубины рекурсии).
По семантике это классический чисто функциональный язык, оперирующий связными списками значений.
// Compile-time list manipulation {
// 3, 3, 3, 3, 3
static int five_threes[] = {
M_listEvalCommaSep(M_listReplicate(v(5), v(3))),
};
// 5, 4, 3, 2, 1
static int from_5_to_1[] = {
M_listEvalCommaSep(M_listReverse(M_list(v(1, 2, 3, 4, 5)))),
};
// 9, 2, 5
static int lesser_than_10[] = {
M_listEvalCommaSep(M_listFilter(M_appl(v(M_uintGreater), v(10)), M_list(v(9, 2, 11, 13, 5)))),
};
// }
// Macro recursion {
#define factorial(n) M_call(factorial, n)
#define factorial_IMPL(n) M_uintMatch(v(n), v(factorial_))
#define factorial_Z_IMPL() v(1)
#define factorial_S_IMPL(n) M_uintMul(M_uintInc(v(n)), factorial(v(n)))
M_assertEq(factorial(v(4)), v(24));
// }
Тот же автор написал также библиотеку datatype99, дополняющую metalang99 алгебраическими типами данных:
#include <datatype99.h>
datatype(
BinaryTree,
(Leaf, int),
(Node, struct BinaryTree *, int, struct BinaryTree *)
);
int sum(const BinaryTree *tree) {
match(*tree) {
of(Leaf, x) {
return *x;
}
of(Node, lhs, x, rhs) {
return sum(*lhs) + *x + sum(*rhs);
}
}
}
- С технической точки зрения, metalang99 — набор макросов для языка C, а не самостоятельный язык. На практике, он примерно настолько же «библиотека», насколько LaTeX — библиотека для TeX.
- metalang99 поддерживает глубину рекурсии до 65535 уровней, что мешает назвать его Тьюринг-полным. Нужно понимать, однако, что ограничение на глубину рекурсии существует в любом языке (количество памяти в практических компьютерах ограничено), так что это разделение довольно призрачно.
- Автор написал две статьи на Хабре про создание этого языка:
- Язык не поддерживает ввод-вывод, так как его не поддерживает препроцессор C.
- Это не первая попытка написать язык поверх препроцессора C: https://github.com/Hirrolot/awesome-c-preprocessor
Страница на Esolangs: https://esolangs.org/wiki/RSSB
RSSB — это Тьюринг-полный язык из одной инструкции. Он использует два регистра, ACC
и IP
.
Регистр IP
— это instruction pointer, адрес инструкции, которая сейчас выполняется.
Также программе доступна память, представляющая из себя плоский массив знаковых чисел.
Некоторые адреса в памяти специальные:
Адрес | Значение |
---|---|
0 | Регистр IP |
1 | Регистр ACC |
2 | Всегда 0 |
3 | Ввод символа |
4 | Вывод символа |
; RSSB Hello World, John Metcalf
loop rssb acc ; acc = character from ptr
ptr rssb hello
rssb out ; display character
rssb zero ; acc = -acc
rssb zero ; always skipped
rssb sum ; subtract acc from sum
rssb ip ; skipped if sum is <0
; otherwise jump to 0
rssb acc ; subtract 1 from ptr
rssb one
rssb ptr
rssb acc ; jump to loop
rssb loopoff
rssb ip
loopoff rssb $-loop
sum rssb -1116
one rssb 1
rssb 33 ; '!'
rssb 100 ; 'd'
rssb 108 ; 'l'
rssb 114 ; 'r'
rssb 111 ; 'o'
rssb 87 ; 'W'
rssb 32 ; ' '
rssb 44 ; ','
rssb 111 ; 'o'
rssb 108 ; 'l'
rssb 108 ; 'l'
rssb 101 ; 'e'
hello rssb 72 ; 'H'
- Единственная инструкция RSSB,
reverse subtract and skip if borrow
, транслируется на C примерно так:/* где-то в начале */ #define IP (*0) /* ячейка в памяти по адресу 0 */ #define ACC (*1) /* ячейка в памяти по адресу 1 */ /* код самой инструкции */ *arg = ACC = *arg - ACC; if (ACC < 0) { IP += 2; } else { IP += 1; }
- Сам код программы на RSSB загружается в память, позволяя программе модифицировать саму себя
- RSSB поддерживает метки (заменяя их на адреса в памяти перед выполнением программы) и даже определяет несколько по умолчанию — для адресов 0-4, имеющих специальное значение.
- Ввод-вывод (поддержка которого, строго говоря, необязательна для Тьюринг-полноты) реализован через специальные адреса в памяти. Это довольно распространённый способ для эзотерических языков.
- В отличие от многих других языков с одной инструкцией, RSSB требует указывать название команды
(
rssb
) на каждой строчке исходного кода. - Да, RSSB это не единственный ЯП, поддерживающий только одну инструкцию: https://esolangs.org/wiki/OISC