- PVSM.RU - https://www.pvsm.ru -
Только что вышла первая версия разработанного мной статически типизированного встраиваемого скриптового языка 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.
Автор: Василий Терешков
Источник [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
Нажмите здесь для печати.