Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров15K

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

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

Мотивы


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

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

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

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

Язык


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

Транслятор Umka компилирует в байт-код, который затем исполняется стековой виртуальной машиной. Все проверки типов делаются на этапе компиляции. Данные в стеке уже не несут никакой информации о типах. Транслятор поставляется в виде динамической библиотеки со своим API и небольшой «обёртки» — исполняемого файла. Исходный код написан на C99 и переносим на разные платформы. Сейчас выпущены сборки для процессора 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)
}

Примеры


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

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

image
Теги:
Хабы:
+45
Комментарии99

Публикации

Изменить настройки темы

Истории

Работа

Программист С
49 вакансий
Go разработчик
129 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн