The Libretto Programming Language

Libretto для веб-приложений

01 Начало работы
02 Как изучать примеры
03 Имена в Libretto
04 Типы данных
05 Операторы
06 Пути
07 Методы
08 Статическая утиная типизация

07 Методы

Определение метода начинается с ключевого слова def, после которого идет заголовок метода, а затем его тело:

def hello = "Привет!" 
Заголовок состоит из имени метода hello. Строка "Привет!" – тело метода. Заголовок и тело метода разделяются знаком равенства.

Метод можно вычислить:

Контекст метода и ключевое слово this

Методы, как и любые другие выражения Libretto, всегда вычисляются в контексте. Контекст метода может быть типизирован. Это делается сразу после ключевого слова def:

def Int add5 = this + 5
Метод add5 определен только на целых числах. Ключевое слово this позволяет получить текущее значение контекста метода:

Описание типа контекста может быть опущено. Тип по умолчанию – Any:

Определения id1 и id2 эквивалентны.

Пользовательские структуры также могут служить контекстом метода. В Libretto определение методов на структурах играет примерно такую же роль, как определение методов на классах в Java. Например,

Метод fullname работает в контексте персон, порождая строчку с полным именем персоны.

Аргументы метода

Метод может иметь любое количество аргументов, например:

В этом примере для каждого аргумента явно задан его тип данных (вид структуры и кардинальность). Первый аргумент strings может получать на вход произвольную последовательность строк, а второй аргумент – suf – равно одну строку.

Компилятор во время трансляции программы проверяет, соответствует ли тип актуальных параметров описаниям. Если нет, то выдается ошибка компиляции. Примеры ошибочных вызовов:

  suffix("a",("b","c")) // первый – корректный, у второго некорректна кардинальность
  suffix(1, "b") // неверный тип первого аргумента

Описание типов аргументов является обязательным.

Пользовательские структуры также могут быть аргументами:

Метод может одновременно работать и с контекстом, и с аргументами:

Методы и последовательности

Если в контексте метода появляется последовательность, метод вычисляется поэлементно – отдельно вызывается для каждого элемента контекстной последовательности:

В этом случае результат вычислений для всех входных элементов группируется в единую выходную последовательность (в примере – (15,25,35,45,55)).

Результатом вычисления метода также может быть последовательность любой длины:

В данном примере выходная последовательность формируется "попарно": ((1,1),(2,2),(3,3),(4,4),(5,5)). Однако поскольку последовательности в Libretto плоские, эта запись эквивалентна (1,1,2,2,3,3,4,4,5,5).

Тип результата метода

Для метода может быть указан тип результата. Например, результат метода duplicate может быть определен так:

Компилятор старается вычислить тип результата самостоятельно. Однако в некоторых случаях указание типа результата обязательно, например, при определении рекурсивных методов:

Метод fact определяется рекурсивно, поэтому требуется явное описание типа результата.

Полиморфные определения методов

Полиморфное определение – это когда метод имеет несколько определений, каждое из которых обрабатывает данные определенного типа. Например,

Для контекстных значений компилятор старается подобрать метод с наиболее точным описанием типа. Например, для целого 115 подходят варианты с Int и Any (отсутствие явного описания типа эквивалентно Any). Но, поскольку Int более точно характеризует 115, то применяется первое правило, а не третье. Для вещественного 3.14 более точного описания, чем Any нет, поэтому применяется третье определение.

Оператор return

Оператор return позволяет завершить вычисление метода в любом месте и явно указать значение результата, например,

В этом примере делим одно число на другое нацело, проверяя делитель на ноль. Поскольку результатом может быть как число, так и строка, то тип результата получается объединенный (String | Int)!.

Для методов, содержащих return, указание типа результата обязательно.

Заметим, что во многих случаях без оператора return можно безболезненно обойтись. Мы настоятельно рекомендуем не пользоваться return без крайней нужды, поскольку он усложняет семантику программы, а иногда и снижает ее эффективность.

В частности, последний пример может быть заменен на

Специальные методы

Libretto поддерживает специальные методы (как поэлементные, так и коллекционные), которые облегчают обработку последовательностей.

Коллекционные методы отличаются от поэлементных тем, что перед вызовом собирают в качестве контекста всю последовательность, с которой затем работают как с единым целым.

Сортировка

Коллекционный метод ^(<значение>) позволяет сортировать контекстную последовательность по значению–аргументу. Например,

Здесь сортировка проводится по строкам ('$' говорит о том, что сравниваются сами элементы последовательности).

Более интересный пример:

Здесь порядок наводится по фамилии, а для совпадающих фамилий – по имени.

Индексы

Специальный коллекционный метод [..] играет роль, аналогичную индексам в массивах:

Фильтр

Фильтр ?[<условие>] пропускает только те элементы контекстной последовательности, которые удовлетворяют условию. Например,

Здесь фильтр пропускает значения не меньше, чем 5.

Фильтр структуры

Фильтр ?<имя структуры> пропускает только элементы последовательности, имеющие определенную структуру. Например, ?String пропускает только строковые значения. Пример:

Ловушка пустоты

Этот коллекционный метод срабатывает в случае, когда контекстная последовательность оказывается пустой. Синтаксис ловушки – exp1. ?(handler). path2. Если вычисление предыдущего пути path1 завершилось пустотой, то значением всей последовательности станет значение handler. В противном случае, ловушка игнорируется, и вычисления продолжаются как обычно.

Например,

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

Комбинирование коллекционных методов

Как и обычные методы, коллекционные методы могут комбинироваться внутри пути. В следующем примере из последовательности выбирается подпоследовательность, которая затем сортируется по убыванию:

Анонимные методы

В Libretto можно работать с методами как с обычными структурами – присваивать их переменным, передавать в качестве параметров. Для этого используются анонимные методы.

Анонимные методы без аргументов

Анонимный метод без аргументов получится, если мы припишем знак процента к обычному блоку, например, #{x + 5}. Процент предотвращает немедленное вычисление (оценивание) блока и превращает его в портративный объект. Для того, чтобы вычислить (оценить) такой объект, применяется оператор !. Например,

Анонимный метод присвоен переменной fun, а затем оценен в контексте строки "a". Значение контекста берется с помощью $.

Контекст метода может быть типизирован:

По умолчанию контекст типизируется как Any, что может приводить к неожиданным результатам:

При сложении Libretto пытается свести экземпляр Any к строке, поэтому в этом примере вычисляется "10" + 5 == "105".

Замыкание

При создании анонимного метода, в нем фиксируются значения всех переменных, которые определены вне метода, но входят в него:

Анонимный метод #{x + 5} содержит вхождение переменной x. Несмотря на то, что у метода call имеется своя переменная x, используется то значение x, которое было зафиксировано в момент создания анонимного метода, то есть 10.

Если переменная x является изменяемой, то анонимный метод будет чувствительным к изменениям этой переменной:

Переменная x изменила значение после создания анонимного метода, но до его оценивания. Тем не менее, при оценивании использовалось актуальное значение переменной x.

Анонимные методы с аргументами

Можно определять анонимные методы с аргументами:

Здесь x – аргумент анонимного метода. Аргументам анонимного метода нужно в обязательном порядке задавать типы. При вызове анонимного метода, его актуальные параметры перечисляются после восклицательного знака в круглых скобках. Анонимные методы с N аргументами являются экземплярами структуры с именем LambdaN.

Анонимные методы можно передавать в другие методы в качестве параметров:

На анонимных методах с аргументами также работает замыкание:

Оператор return в анонимных методах

В анонимных методах можно использовать return. Однако важно помнить, что этот return будет относиться не к самому анонимному методу, а к тому методу, в котором этот анонимный метод определен:

Анонимные методы с return могут выполняться только внутри методов, в которых они определены.

Синтаксический сахар

Синтаксический сахар – альтернативный синтаксис представления конструкций языка, имеющий целью сделать эти конструкции проще и понятнее для восприятия.

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

В частности, в языке отсутствуют специальные конструкции для цикла while и средств управления исключениями try и finally. В Libretto это обычные методы. Благодаря синтаксическому сахару они могут использоваться в привычном для всех виде.

Двоеточие

В Libretto следующие форматы вызова метода fun(x,y,z) являются эквивалентными:

fun(x,y,z)
fun(x,y) : z
fun(x): y, z
fun: x, y, z

Например,

Очень эффективно сочетание синтаксического сахара "двоеточие" с синтаксическим сахаром "процент", действующим на анонимных методах.

Звездочка (коллекционный контекст)

Выразительное средство, позволяющее:

В Libretto вызов метода fun(x, y) эквивалентен x.*fun(y).

Например, определим метод sum(seq:Int*), суммирующий элементы последовательности:

Метод одноаргументный, но использование звездочки позволяет использовать его как нульместный метод, суммирующий контекстные последовательности.

Важно то, что на вход методу подается все коллекция сразу, а не ее элементы один за другим. Например, в третьем случае на вход подается пустая последовательность, что невозможно для обычных контекстных методов.

В качестве другого примера, реализуем операции пересечения, объединения и разности множеств. В роли множеств будут выступать последовательности.

Реализация не самая эффективная, однако интересная с точки зрения демонстрации конвейерных инструментов работы с последовательностями. В данном примере с помощью синтаксического сахара "звездочка":

Инфиксный оператор

Любой двуместный метод может быть вызван в инфиксной форме, например,

В частности, с помощью этого синтаксического сахара можно записывать операции на множествах в "привычном" для математиков виде:

В пункте "Мэпы" мы использовали инфиксную запись для метода to.

Решетка

Определим

Данная программа может быть представлена в существенно более компактном и читабельном виде:

Используя # в аргументе метода, мы подсказываем Libretto, что этот аргумент не должен вычисляться, а должен интерпретироваться как анонимный метод.

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

Сочетание "двоеточие+решетка" позволяет также компактно работать с транзакциями в базах данных:

Код, который должен быть исполнен в рамках транзакции, передается методу mem/w в виде анонимного метода.

06 Пути /// 08 Статическая утиная типизация