- PVSM.RU - https://www.pvsm.ru -

Umka: новый статически типизированный скриптовый язык

Umka: новый статически типизированный скриптовый язык - 1
Только что вышла первая версия разработанного мной статически типизированного встраиваемого скриптового языка Umka [1]. Он призван сочетать гибкость привычных скриптовых языков с защитой от ошибок типов на этапе компиляции в байт-код. Основная идея языка — Explicit is better than implicit — позаимствована из «дзена Python», однако должна приобрести здесь несколько иной и более очевидный смысл.

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

Мотивы

Первым достоинством динамической типизации обычно называют сокращение цикла разработки/отладки и экономию времени программиста. Рискуя вызвать неудовольствие публики, должен признаться, что мой собственный опыт этого никак не подтверждает. Всякий раз после ничтожного исправления своего скрипта обучения нейросети на Python я вынужден дожидаться загрузки Python, NumPy, PyTorch, чтения большого массива данных из файлов, передачи его в GPU, начала обработки — и лишь тогда обнаруживать, что PyTorch ожидал тензор размера (1, 1, m, n, 3) вместо (1, m, n, 3).

Я охотно признаю, что многие предпочитают динамически типизированные языки по своим личным причинам. Возможно даже, что склонность или неприязнь к динамической типизации — явление того же порядка, что и отношение к оливкам и томатному соку. Попытки объективного исследования этого вопроса приводят, видимо, к неубедительным результатам [2].

В то же время популярность TypeScript, введение аннотаций типов в Python, горячие дискуссии на Reddit [3] и Хабре [4] заставляют думать, что фактическое отождествление скриптовых языков с динамически типизированными вовсе не догма, а стечение обстоятельств, и статически типизированный скриптовый язык имеет полное право существовать.

Так появился язык, названный в честь кота, названного в честь медведя.

Язык

Синтаксис языка в целом был навеян Go. С примерами синтаксических конструкций можно познакомиться на странице проекта [1]. При объявлении переменных может использоваться сокращённая запись с выводом типов. Заслуживает внимания отступление от правил Go, сделанное в синтаксисе указателей. Создатели Go сетовали [5], что буквальное следование примеру C обернулось здесь излишним усложнением синтаксиса и что разумнее было бы ввести постфиксный оператор разыменования указателей вроде паскалевского p^ вместо *p. Именно так и сделано в Umka.

Транслятор Umka компилирует в байт-код, который затем исполняется стековой виртуальной машиной. Все проверки типов делаются на этапе компиляции. Данные в стеке уже не несут никакой информации о типах. Транслятор поставляется в виде динамической библиотеки со своим API и небольшой «обёртки» — исполняемого файла. Исходный код написан на C99 и переносим на разные платформы. Сейчас выпущены сборки [6] для процессора x86-64 (Windows и Linux).

Управление памятью пока сделано на основе счётчиков ссылок. Если язык вызовет какой-то интерес и будут попытки его использовать, появится смысл устроить более совершенный сборщик мусора. Язык поддерживает классические составные типы данных (массивы и структуры), размещаемые на стеке, и динамические массивы, размещаемые в куче. Любой классический массив или структуру можно разместить и в куче явным вызовом new().

Полиморфизм обеспечивается интерфейсами в стиле Go. Понятий класса, объекта и наследования нет.

Многозадачность построена на понятии «волокон» (fibers) — упрощённых потоков, запускаемых в пределах одной виртуальной машины и явно вызывающих друг друга. По сути это синонимично понятию сопрограмм (coroutines). Поскольку логика применения этих сопрограмм немного отступает от традиции Go и становится ближе к Lua и Wren, есть смысл привести образец кода:

fn childFunc(parent: std.Fiber, buf: ^int) {
    for i := 0; i < 5; i++ {
        std.println("Child : i=" + std.itoa(i) + " buf=" + std.itoa(buf^))
        buf^ = i * 3
        fibercall(parent)
    }
}

fn parentFunc() {
    a := 0
    child := fiberspawn(childFunc, &a)    
    for i := 0; i < 10; i++ {
        std.println("Parent: i=" + std.itoa(i) + " buf=" + std.itoa(a))
        a = i * 7
        if fiberalive(child) {
            fibercall(child)
        }
    }    
    fiberfree(child)
}

Примеры

В качестве основного примера, демонстрирующего возможности языка, рекомендую посмотреть программу рендеринга трёхмерных сцен [7] на основе обратной трассировки лучей. Здесь весьма органично используются рекурсия, интерфейсы, динамические массивы; несколько более искусственно — многозадачность.

Примером встраивания транслятора Umka в проект на C может послужить исходный код исполняемой «обёртки» [8] самого транслятора. Там же есть и образец расширения языка Umka внешними функциями на C.

image

Автор: Василий Терешков

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/352872

Ссылки в тексте:

[1] Umka: https://github.com/vtereshkov/umka-lang

[2] неубедительным результатам: https://softwareengineering.stackexchange.com/questions/10032/dynamically-vs-statically-typed-languages-studies/224460#224460

[3] Reddit: https://www.reddit.com/r/ProgrammingLanguages/comments/a3yihk/existential_crisis_is_making_a_statically_typed/

[4] Хабре: https://habr.com/ru/post/500926/

[5] сетовали: https://blog.golang.org/declaration-syntax

[6] сборки: https://github.com/vtereshkov/umka-lang/releases

[7] программу рендеринга трёхмерных сцен: https://github.com/vtereshkov/umka-lang/blob/master/examples/raytracer.um

[8] исходный код исполняемой «обёртки»: https://github.com/vtereshkov/umka-lang/blob/master/src/umka.c

[9] Источник: https://habr.com/ru/post/501002/?utm_source=habrahabr&utm_medium=rss&utm_campaign=501002