Skip to content

Instantly share code, notes, and snippets.

@GoldsteinE
Created February 16, 2021 15:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GoldsteinE/ffcaab640b74485610eacebe1708f088 to your computer and use it in GitHub Desktop.
Save GoldsteinE/ffcaab640b74485610eacebe1708f088 to your computer and use it in GitHub Desktop.
Языки программирования, про которые вы (возможно) не знали — выпуск 1

Языки программирования, про которые вы (возможно) не знали — выпуск 1

Язык первый, практичный — Nim

Сайт: 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

Summary

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.

Язык второй, исследовательский — metalang99

Сайт: 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

Summary

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 уровней, что мешает назвать его Тьюринг-полным. Нужно понимать, однако, что ограничение на глубину рекурсии существует в любом языке (количество памяти в практических компьютерах ограничено), так что это разделение довольно призрачно.
  • Автор написал две статьи на Хабре про создание этого языка:
    1. https://habr.com/en/post/520850/
    2. https://habr.com/en/post/523606/
  • Язык не поддерживает ввод-вывод, так как его не поддерживает препроцессор C.
  • Это не первая попытка написать язык поверх препроцессора C: https://github.com/Hirrolot/awesome-c-preprocessor

Язык третий, эзотерический — RSSB

Страница на Esolangs: https://esolangs.org/wiki/RSSB

Summary

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment