Документация языка

Полное описание синтаксиса и возможностей Глаголицы — от литералов до монад и асинхронности.

Введение

Глаголица — функциональный язык программирования с синтаксисом на кириллице. Он поддерживает рекурсию, замыкания, сопоставление с образцом, монадическую обработку ошибок, асинхронные вычисления, недетерминированные вычисления (List-монада), каррирование и пайплайн-операторы.

Все ключевые слова записываются заглавными буквами на русском языке (например, ПУСТЬ, ЕСЛИ, ИНАЧЕ, СОПОСТАВИТЬ). Имена переменных и функций можно писать как кириллицей, так и латиницей.

Содержание

  1. Структура программы
  2. Комментарии
  3. Базовые типы и литералы
  4. Идентификаторы
  5. Объявление переменных и функций
  6. Условные выражения
  7. Арифметические и логические операторы
  8. Списки
  9. Сопоставление с образцом
  10. Лямбда-функции
  11. Каррирование и частичное применение
  12. Пайплайн-операторы
  13. Цикл и диапазоны
  14. Монада успеха/ошибки
  15. Асинхронность
  16. Недетерминированные вычисления
  17. Структурные типы
  18. Импорт модулей
  19. Встроенные функции
  20. Запуск программ

1. Структура программы

Программа на Глаголице — это последовательность выражений. Каждое выражение возвращает значение (либо VUnit, если значение бессмысленно — например, после вывод).

ПУСТЬ х = 10
ПУСТЬ у = 20
вывод(х + у)

Программа исполняется сверху вниз. Все объявления (ПУСТЬ, ТИП) видны в последующем коде в том же блоке.

2. Комментарии

Глаголица поддерживает два вида комментариев.

Однострочный

Начинается с комментарий: и идёт до конца строки:

комментарий: это однострочный комментарий
ПУСТЬ х = 5

Многострочный

Начинается с комментарий { ... }:

комментарий {
    Здесь несколько строк
    с пояснениями.
}

3. Базовые типы и литералы

ТипЛитералыПример
Целоецелые числа0, 42, -7
ЛогическоеИСТИНА, ЛОЖЬИСТИНА
Строкав двойных кавычках"Привет Мир!"
Пустой списокПУСТО или []ПУСТО
Список[элем1, элем2, ...][1, 2, 3]

4. Идентификаторы

Идентификатор может содержать:

Примеры допустимых имён: х, мой_список, factorial, результат_2.

Зарезервированные ключевые слова нельзя использовать в качестве идентификаторов: ПУСТЬ, В, ЕСЛИ, ТО, ИНАЧЕ, СОПОСТАВИТЬ, С, КОГДА, УСПЕХ, ПРОВАЛ, ФОНОМ, ДОЖДАТЬСЯ, ТРЕБОВАТЬ, ВАРИАНТЫ, ОТ_ДО, ИСТИНА, ЛОЖЬ, И, ИЛИ, ПУСТО, ДАЛЕЕ, ДАЛЕЕ_УСПЕШНО.

5. Объявление переменных и функций (ПУСТЬ)

Ключевое слово ПУСТЬ объявляет имя для значения или функции.

Переменная

ПУСТЬ имя = выражение

Пример:

ПУСТЬ х = 10
ПУСТЬ сообщение = "Привет"

Функция

ПУСТЬ имя(аргумент1, аргумент2, ...){
    тело
}

Пример:

ПУСТЬ сложить(а, б){
    а + б
}
вывод(сложить(2, 3))

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

ПУСТЬ факториал(х){
    ЕСЛИ х == 0 ТО { 1 }
    ИНАЧЕ { х * факториал(х - 1) }
}

6. Условные выражения (ЕСЛИ ... ТО ... ИНАЧЕ)

Условие — это выражение, возвращающее значение одной из двух ветвей.

ЕСЛИ условие ТО { ветвь_1 } ИНАЧЕ { ветвь_2 }

Обе ветви обязательны и заключены в фигурные скобки. Условие должно вычисляться в логическое значение.

ПУСТЬ макс(а, б){
    ЕСЛИ а > б ТО { а } ИНАЧЕ { б }
}
вывод(макс(7, 12))

Условия можно вкладывать друг в друга:

ЕСЛИ х == 0 ТО { 0 }
ИНАЧЕ {
    ЕСЛИ х == 1 ТО { 1 }
    ИНАЧЕ { фиб(х - 1) + фиб(х - 2) }
}

7. Арифметические и логические операторы

ОператорНазначениеПример
+сложение2 + 3
-вычитание5 - 1
*умножение4 * 6
/целочисленное деление10 / 3
==равенствох == 0
>большеа > б
Илогическое Иа И б
ИЛИлогическое ИЛИа ИЛИ б

Приоритет от высшего к низшему: *, /+, -==, >ИИЛИДАЛЕЕ, ДАЛЕЕ_УСПЕШНО.

8. Списки

Список создаётся через квадратные скобки с разделителем-запятой:

ПУСТЬ числа = [1, 2, 3, 4, 5]
ПУСТЬ строки = ["раз", "два", "три"]
ПУСТЬ пусто = ПУСТО

Пустой список можно записать как ПУСТО или [].

Внутри Глаголица представляет списки как пары «голова — хвост», поэтому для разбора удобно использовать сопоставление с образцом.

9. Сопоставление с образцом (СОПОСТАВИТЬ ... С)

Сопоставление с образцом проверяет значение по нескольким шаблонам сверху вниз и выполняет ветвь первого подходящего.

СОПОСТАВИТЬ значение С {
    КОГДА шаблон1 -> { выражение1 }
    КОГДА шаблон2 -> { выражение2 }
    ...
}

Поддерживаемые шаблоны

ШаблонЧто матчит
0, 42конкретное целое число
ИСТИНА, ЛОЖЬлогические литералы
"строка"конкретную строку
ПУСТОпустой список
[голова - хвост]непустой список (привязывает имена)
имялюбое значение, привязывая его к имени
_любое значение без привязки

Пример:

ПУСТЬ описать(х){
    СОПОСТАВИТЬ х С {
        КОГДА 0 -> { "ноль" }
        КОГДА 1 -> { "один" }
        КОГДА _ -> { "другое" }
    }
}

Разбор списка:

ПУСТЬ длина(сп){
    СОПОСТАВИТЬ сп С {
        КОГДА ПУСТО -> { 0 }
        КОГДА [_ - хвост] -> { 1 + длина(хвост) }
    }
}

10. Лямбда-функции

Анонимная функция записывается как:

(аргумент1, аргумент2, ...) -> выражение

Пример:

ПУСТЬ удвоить = (х) -> { х * 2 }
вывод(удвоить(7))

Лямбды чаще всего используются вместе с пайплайн-операторами и встроенными функциями высшего порядка (отобразить, отфильтровать, свернуть).

11. Каррирование и частичное применение

Чтобы получить частично применённую функцию, подставьте _ вместо ещё не известного аргумента:

ПУСТЬ вычесть(п, р){ п - р }
ПУСТЬ отнятьОтДесяти = вычесть(10, _)
вывод(отнятьОтДесяти(3))   комментарий: 7

_ — плейсхолдер: на этом месте функция «запоминает» недостающий аргумент и возвращает новую функцию, которая ждёт его.

12. Пайплайн-операторы (ДАЛЕЕ, ДАЛЕЕ_УСПЕШНО)

ДАЛЕЕ передаёт значение слева в функцию справа:

значение ДАЛЕЕ функция

Пример:

ПУСТЬ удвоить(х){ х * 2 }
ПУСТЬ результат = 5 ДАЛЕЕ (значение) -> { удвоить(значение) }
вывод(результат)   комментарий: 10

ДАЛЕЕ_УСПЕШНО — «безопасный» пайплайн для значений типа УСПЕХ / ПРОВАЛ. Если слева УСПЕХ(v), выполняется правая часть с распакованным v. Если слева ПРОВАЛ, цепочка прерывается и ошибка прокидывается дальше.

ПУСТЬ результат = УСПЕХ(10) ДАЛЕЕ_УСПЕШНО (х) -> { УСПЕХ(х * 2) }

У ДАЛЕЕ_УСПЕШНО есть синонимичный знаковый оператор >>?.

13. Цикл ДЛЯ и диапазоны (ОТ_ДО)

Цикл пробегает по элементам списка и исполняет тело для каждого:

ДЛЯ (имя В коллекция) {
    тело
}

Чаще всего коллекция — диапазон, построенный через ОТ_ДО(а, б):

ДЛЯ (ч В ОТ_ДО(1, 10)) {
    вывод(ч * ч)
}

ОТ_ДО(1, 10) — это список [1, 2, ..., 10].

14. Монада успеха/ошибки (УСПЕХ, ПРОВАЛ)

Глаголица предоставляет встроенный тип результата:

ПУСТЬ безопасное_деление(а, б){
    ЕСЛИ б == 0 ТО { ПРОВАЛ("Деление на ноль") }
    ИНАЧЕ { УСПЕХ(а / б) }
}

Цепочки обработки строятся через ДАЛЕЕ_УСПЕШНО:

ПУСТЬ итог = безопасное_деление(10, 2)
    ДАЛЕЕ_УСПЕШНО (х) -> { УСПЕХ(х + 1) }
вывод(итог)

При появлении ПРОВАЛ все последующие шаги пропускаются.

15. Асинхронность (ФОНОМ, ДОЖДАТЬСЯ)

ФОНОМ({ ... }) запускает блок кода в фоновой задаче и возвращает «фьючер»:

ПУСТЬ задача = ФОНОМ({
    комментарий: тут может быть долгое вычисление
    42
})

ДОЖДАТЬСЯ(...) блокирует выполнение, пока задача не завершится, и возвращает её результат:

ПУСТЬ результат = ДОЖДАТЬСЯ(задача)
вывод(результат)   комментарий: 42

16. Недетерминированные вычисления (ВАРИАНТЫ, ТРЕБОВАТЬ)

Глаголица использует List-монаду: программа может «расщепляться» на несколько вселенных, по одной для каждого возможного значения.

ВАРИАНТЫ(а, б, в, ...) — это значение, которое одновременно равно каждому из перечисленных:

ПУСТЬ х = ВАРИАНТЫ(1, 2, 3)
ПУСТЬ у = ВАРИАНТЫ(1, 2, 3)
ТРЕБОВАТЬ(х + у == 4)
вывод([х, у])

ТРЕБОВАТЬ(условие) отсекает все вселенные, в которых условие ложно. В примере выше будут выведены все пары (х, у) такие, что х + у == 4.

Это удобный способ записывать переборные и комбинаторные задачи: декартовы произведения, поиск решений, задачи логики.

17. Структурные типы (ТИП)

Объявление структурного типа (записи):

ТИП ИмяТипа = { поле1 Тип1, поле2 Тип2, ... }

Пример:

ТИП Игрок = { здоровье Int, уровень Int }

Создание экземпляра:

ПУСТЬ герой = Игрок { здоровье = 100, уровень = 5 }

Доступ к полю — через точку:

вывод(герой.здоровье)

Имя типа в аннотациях полей — пометка для читателя; во время выполнения значения хранятся как именованные пары без жёсткой проверки типов.

18. Импорт модулей (ИМПОРТ)

Подключение содержимого другого файла:

ИМПОРТ "путь/к/файлу.gl"

Все объявления из импортированного файла становятся доступны после директивы:

ИМПОРТ ".\examples\Glagol_examples\factorial_for_import.gl"

ПУСТЬ результат = факториал(5)
вывод(результат)

Путь — относительный (от текущей директории запуска) или абсолютный.

19. Встроенные функции

ФункцияНазначение
вывод(значение)печатает значение в консоль
чтение()читает строку из стандартного ввода
читать_файл(путь)читает файл, возвращает УСПЕХ(содержимое) или ПРОВАЛ
Число(строка)парсит строку в целое
Дробное(строка)парсит строку в дробное число
Строка(значение)приводит значение к строке
отобразить(ф)(список)применяет функцию к каждому элементу списка
отфильтровать(ф)(список)оставляет элементы, для которых функция вернула ИСТИНА
свернуть(ф)(список)сворачивает список слева, начиная с первого элемента
взять_пока(ф)(список)берёт элементы с начала, пока предикат истинен

Все функции высшего порядка (отобразить, отфильтровать и т.д.) каррированы — сначала передаётся функция, затем список.

ПУСТЬ удвоить(х){ х * 2 }
ПУСТЬ удвоенные = отобразить(удвоить)([1, 2, 3])
вывод(удвоенные)

20. Запуск программ

Запуск файла

dotnet run --project src -- путь/к/файлу.gl

Файл должен иметь расширение .gl.

REPL-режим

dotnet run --project src -- --repl

В REPL пустая строка завершает сессию. Многострочный ввод можно завершить строкой ;;.

Для удобной разработки доступно расширение для VS Code, а готовые примеры — в разделе Примеры программ.