Я создаю язык своей мечты.

Последние пару недель я наслаждался мощью GNU Common Lisp. В Common Lisp весь код представляет собой список, который является типом данных в списке. По сути, программы на Лиспе - это просто набор аргументов внутри списков. Это, прежде всего, делает Лисп чрезвычайно жизнеспособным языком для метапрограммирования. Дополнительным дополнением к этому эффекту являются макросы Лиспа. Макросы Lisp упрощают вызов подпроцессов из языка и использование кода в качестве типа данных. Это, конечно, может быть чрезвычайно эффективным и может сделать ваш код не только чистым, кратким и читабельным, но и автоматизированным.

Язык, который я создаю, Leya - это план довести эту методологию до крайности. Lisp великолепен и все такое, но есть некоторые вещи, которые мне особенно не нравятся в Common Lisp:

  • Пакеты - это АБСОЛЮТНАЯ БОЛЬ. Я хотел бы иметь простую функцию импорта со структурой, подобной этой:
(import :package)

В Лиспе добавление пакетов - абсолютный кошмар. Конечно, это не обязательно вина Lisp, поскольку язык был создан в 1958 году, но это, безусловно, обратная сторона. Пакеты хранятся локально извне из любой среды в виде простого файла и должны загружаться непосредственно из источника. Это может не стать таким сюрпризом, пока вы не поймете, что зависимости lib (например, lib-sdl2), которые установлены в масштабе всей системы, не могут использоваться в Lisp. Есть альтернативы этому, такие как бета-пакет Quick Lisp, но они определенно не заменят работающий менеджер пакетов.

  • На самом деле Lisp мог бы быть намного быстрее с помощью нескольких настроек производительности на сервере.

Lisp обладает статистической мощью и удивительной скоростью! Эти двое могут быть смертельной комбинацией для Data Science. На самом деле Lisp может быть намного быстрее, чем он есть сейчас. Чтобы понять, почему это так, нам нужно понять концепцию, называемую потоком управления. У каждого языка есть поток управления, и потоки управления могут как «прыгать», так и игнорировать в зависимости от конкретной ситуации. И «прыжок» является ключевым словом, потому что всякий раз, когда выполняется «прыжок», последовательность операций программы изменяется субпоследовательно.

Поток управления по умолчанию вертикальный и отсчитывается от 1 с каждым разделом. Вот пример, написанный на скомпилированной ассемблере NASM:

_start:
; Allocate our memory
; Get the current brk address
        mov rax, 12 ; brk
        mov rdi, 0
        syscall
; rax => alloc_ptr && heap_start
        mov [alloc_ptr], rax
        mov [heap_start], rax
; (Allocate memory)
        mov rdi, rax
        ; 64 == 9 million;
        add rdi, 1000000
; Syscall
        mov rax, 12
        syscall
; Read the source code into memory
;   VVVVVVVVVVVV Recreate this with call reading_loop attached
;            for read evaluate print
    reading_loop:
; Read from stdin
        mov rax, 0
        mov rdi, 0 ; stdin
        mov rsi, [alloc_ptr]
        mov rdx, 100000
        syscall
add [alloc_ptr], rax
cmp rax, 0
        jne reading_loop
    ;   call reading_loop
    ; After the loop:
; Save the end of the program
        mov rax, [alloc_ptr]
        mov [program_end], rax
; Add a null terminator
        mov byte [rax], 0
        inc rax
; Align pointer
    align_loop:
        mov rdi, rax
        and rdi, 0

Хорошо, похоже, много кода.

Но чтобы понять поток управления, нам нужно взглянуть только на несколько примеров. Каждый раз, когда вызывается «_start:», устанавливается нисходящий поток управления, и в память помещается один байт, содержащий число 0. После завершения «_start:», поскольку нет переходов или вызовов, поток управления будет продолжить спуск, и когда он завершит свою связь системного вызова с ядром, он войдет в позицию 1: «read_loop :.»

В конце цикла чтения мы увидим три команды: add, cmp, jne. Во-первых, что делает add? Add добавит значение из адреса памяти, который мы описали в разделе .bss или .data, в rax реестра, который занимает первую позицию в 64-битных регистрах. Следующая команда cmp - сокращение от compare. Это сравнит два значения и изменит флаг в зависимости от результата флага. В этом конкретном случае я использую флаг реестра, хранящийся в байте 0, флаг CF.

И, наконец, что не менее важно, jne - условный переход. Если этот флаг встречается, то команда jne изменит поток управления на круг, сохраняя поток управления в позиции 1: «read_loop :.»

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

Итак, конечно, поток управления не только очень важен для того, что мы хотим сделать, но также очень важен для производительности. Возможно, вы слышали, что циклы for в целом очень хорошо влияют на производительность. Это связано с тем, что цикл for использует комбинацию как минимум четырех позиций и будет выполнять условный переход в зависимости от добавленного байта данных (сохранить длину символов в памяти, а cmp к этому значению сохранить в памяти количество циклов. ) Это означает, что каждый раз, когда это условие последней итерации не выполняется, поток управления будет уменьшаться на 2. Итак, скажем, мы были в позиции 3, каждый раз, когда цикл for не завершается, мы переходим в позицию 1 и повторяем процесс. .

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

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

Заключение

Lisp-подобный язык дает языку программирования Leya все замечательные преимущества Lisp. Простая компиляция, ассемблер собирает всего около 3000 строк кода, все представляет собой список, а также простота и плавность метапрограммирования с помощью макросов.

Создание языка, который делает то же самое, что и другой язык, отчасти бессмысленно, хотя я буду первым, кто скажет, что создание компилятора сделало меня намного лучше в сборке и программировании в целом. На самом деле есть интересная книга под названием «Создайте свой собственный Lisp на C», я бы порекомендовал вам ее проверить, если вы хотите изучить C. познакомит вас со многими основами информатики, которые очень важны для понимания.

Имя «Лея» предназначено для объяснения самого себя, а язык строится с намерением чего-то совершенно другого:

БЫСТРОЕ метапрограммирование для машинного обучения.

Название «Лея» происходит от термина «лемма», который представляет собой математическое разделение аргумента или доказательства, которое также является промежуточным. Я так понимаю, но вот определение Google:

«- Вспомогательная или промежуточная теорема в аргументе или доказательстве».

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

Поскольку Лисп легок и легко компилируется, Лисп является очень быстрым языком для программирования. Обычно считается, что Лисп не может использоваться так же, как C, но синтаксис достаточно высокоуровневый и его легко получить. привыкший. Самая сложная вещь в Лиспе заключается в том, что для его использования вам придется думать обо всем прямо в обратном направлении по сравнению с тем, что вы обычно делали бы. Начиная с возврата и постепенно увеличивая значение. Обычно я устанавливаю переменную только в том конкретном случае, когда значение повторно используется, чтобы избежать его многократного вычисления.

Спасибо, что дочитали до конца, если вы хотите проверить Лею, вот она на Github:



С каждым днем ​​выходит все больше и больше, и медленно, но верно решаются проблемы. На данный момент у меня есть около шести проектов, над которыми я постоянно работаю, поэтому разработка была довольно медленной (не стесняйтесь делать коммит!) Но, надеюсь, к концу этот язык станет моим идеальным выбором для всех моих потребностей в области науки о данных. .