понедельник, 28 января 2013 г.

Реализация на Scala графической среды для языка Unlambda

Реализация на Scala графической среды для языка Unlambda

Для тех, кто меня знает, не секрет, что в свободное от изготовления спектроскопов и телескопов время я занимаюсь программированием. Собственно, сейчас это и есть моя основная деятельность. На этом поприще, конечно, тоже немало всего интересного, о чём можно написать пост. Некоторое время я раздумывал, организовать ли мне два разных блога: в одном из которых я бы иногда писал про всяческие поделки/опыты/явления, а в другом - о программировании, информационных технологиях и.т.п. Я решил писать обо всём в одном месте: во-первых мне так проще; во-вторых я и так пишу достаточно редко: пусть лучше будет один полуживой блог чем два неживых; ну а в третьих - нередко самое интересное именно "на стыке" областей: непонятно, куда постить, скажем, про кофейник, управляемый через веб-интерфейс :) 
В этом посте речь пойдёт о комбинаторной логике, основанном на ней языке программирования Unlambda, который может сломать вам мозг, и реализации "среды" для Unlambda на языке Scala, который, напротив, очень приятен.

Представим, что у нас есть язык, в котором существует единственный тип объектов: функции одного аргумента. И этот самый аргумент функций, и возвращаемое значение - тоже ничто иное, как функции одного аргумента. То есть вообще нет ничего другого - ни чисел, ни переменных, соответственно, нет и присваиваний. Есть, правда, один оператор: применить функцию взяв в качестве аргумента другую функцию. Запись (a b) означает "применить к a аргумент b". Будем считать применение левоассоциативным, то есть

 a b c = ((a b) c).

Для того, чтобы начать что-то конструировать из этого "материала", нам потребуются элементарные кирпичики: базовые функции, через которые мы будем выражать всё остальное. Вот они, K и S:

K x y = x
S x y z = x z (y z)

Минуточку! У нас же функции одного аргумента, что же означает запись K x y = x ? Очень просто: результат применения (K x) - функция, которая при применении к любому y вернёт x. В таком же ключе следует понимать и S x y z.

Всё! Этого достаточно, чтобы наша система была полной по Тьюрингу.
Грубо говоря, это значит, что оператор применения и комбинаторы S и K можно положить в основу языка программирования, на котором можно выразить все те же самые вычисления, что и на других, более привычных языках.

Чтобы понять, как с помощью комбинаторов S и K выразить такие вещи, как числа, ветвления, рекурсию и списки - рекомендую обратиться к популярной статье.

Всё это и положил в основу Unlambda изобретатель данного языка - Дэвид Мэдор (David Madore). Синтаксис языка очень простой, похожий на то, что я уже писал, только операция применения записывается не скобками, а обратным апострофом: Так, выражение ((ab)c) запишется как ``abc, выражение a(bc), соответственно, как `a`bc.

В Unlambda встроены уже знакомые нам функции s и k. Кроме них есть функция i, "тождественный комбинатор":

  I x = x

или, в синтаксисе Unlambda `ix = x. Её существование удобно, но необязательно, так как она может быть выражена чере S и K. Один из способов:

 S K K x = K x (K x) = x

 Для печати результатов служит функция .c, которая, будучи применённой к любому x вернёт x и напечатает символ c. Есть ещё с пяток функций, о которых можно прочесть, например, в статье в википедии.

Я хочу рассказать о созданной мной "графической среде", в которой можно исследовать и отлаживать программы на Unlambda. Интерфейс программы очень простой: там, где написано "Source code" пишем или вставляем из буфера обмена ваш исходный код. Например, на скриншоте ```s`kiii  . Затем, для того, чтобы провести разбор вашей программы и построение дерева вычислений (которое можно видеть в области "Evaluation tree"), нажимаем F2. Затем, если ваша программа читает какие-то данные (см. Википедию про чтение данных в Unlambda), то вводим данные для чтения программой в поле "Input". Теперь, для того, чтобы ваша программа выполнялась шаг за шагом, нажимаем F7. При этом будет видно, как шаг за шагом преобразуется дерево "Evaluation tree".



Где взять саму Unlambda-среду? Можно собрать из исходников, которые выложены на гитхабе.
Всё написано на языке Scala. Сборка осуществляется с помощью Simple Build Tool (SBT). Simple Build Tool - конечно же оксюморон, поэтому ниже приведу последовательность действий (в основном это команды в консоли), которые нужно выполнить, чтобы собрать и запустить мою Unlambda-среду.

Для тех, кто не хочет заморачиваться и доверяет собранным чужими руками бинарникам, я положил на Dropbox архив, в котором всё лежит уже в скомпилированном виде, и можно запустить среду, выплонив файл run.bat (в Windows) или run.sh (в Linux).

А вот инструкция для сборки из исходников:
  1. Ставим SBT. Скачать можно отсюда. Я заметил, что SBT плоховато работает в Windows, если имя пользователя Windows написано кириллицей.
  2. Качаем исходники ScalaSwingTreeWrapper. Можно так же воспользоваться git-ом и склонировать репозиторий. Например, у меня ScalaSwingTreeWrapper распаковался в директорию ScalaSwingTreeWrapper-master.
  3. Собираем ScalaSwingTreeWrapper. В оболочке командной строки переходим туда

    cd ScalaSwingTreeWrapper-master
    и выполняем команду

    sbt
    если sbt не прописана в переменной окружения PATH, то вместо этого

    ПОЛНЫЙ_ПУТЬ_К_SBT/sbt
    В оболочке sbt выполняем команды

    update
    package


    В результате в директории появится файл ScalaSwingTreeWrapper-master/target/scala_2.9.1/scalaswingtreewrapper_2.9.1-1.2.jar.Возможно цифры, обозначающие версии компилдятора Scala и  ScalaSwingTreeWrapper'а будут отличаться.
  4. Качаем исходники среды Unlambda, о которой я здесь пишу. Как и в случае с ScalaSwingTreeWrapper'ом можно так же воспользоваться git-ом и склонировать репозиторий. Распаковываем архив, получаем директорию UnlambdaSimpleEnvironment-master
  5. Копируем файл scalaswingtreewrapper_xxx.jar, полученный на шаге 3 в поддиректорию проекта UnlambdaSimpleEnvironment-master/Unlambda-GUI/lib.
  6. Собираем и запускаем среду Unlambda. Для этого переходим в директорию UnlambdaSimpleEnvironment-master, и запускаем sbt, выполнив команду
    sbt

    В оболочке sbt выполняем команду

    gui/run
Так. А зачем всё это было нужно мне?

Я уже давно присматривался к языку программирования Scala. Уже несколько лет назад я вдоволь написался стандартных примеров в духе "здравствуй мир", вычисления факториала и.т.п. Недавно на сайте coursera.org проходил онлайн-курс по Функциональному программированию на Scala за авторством самого Мартина Одерски (Martin Odersky), создателя Scala. Ознакомившись с материалами курса, я решил, что неплохо было бы более близко познакомится с "экосистемой" Scala, сварганив пусть "маленький и несерьёзный", но "настоящий" проектик, в котором бы использовалась система сборки (в мире Scala стандарт де-факто SBT), автотесты и.т.п. Кроме того, я между делом интересуюсь теоретическими основами функционального программирования. Поэтому
ещё одна реализация Unlambda'ы - идеальный вариант: написать на Scala интерпретатор Unlambda крайне просто, других реализаций именно на Scala я не нашёл (хотя вообще реализаций Unlambda довольно много). Кроме того, мало таких реализаций, в котором можно было бы не только просто выполнять программы на Unlambda, а в интерактивном режиме смотреть, что и как происходит "внутри" вашей программы. Эти обстоятельства и смотивировали меня создать описанную Unlambda-среду используя Scala.

Ссылки

  1. Комбинаторы — это просто! (wikibooks.org) - пожалуй, самое лучшее из того, что подходит для первого знакомства с комбинаторной логикой.
  2. Домашняя страничка языка Unlambda. В "стандартную поставку", которую можно скачать с этого сайта входят несколько реализаций интерпретатора на разных языках. Хотелось бы отметить реализацию на Perl, которая позволяет выполнять программы пошагово, в режиме отладчика с помощью интерфейса командной строки (как в GDB), и очень выразительные реализации на двух диалектах ML.
  3. Unlambda (ru.wikipedia.org) - статья в википедии
  4. Unlambda - мозги навыворот - похожий пост об Unlambda. Реализация на языке Go.
  5. Бестиповое лямбда-исчисление, комбинаторы, Unlambda и числа Фибоначчи - пост про Unlambda на habrahabr.ru. 
  6. Unlambda.NET - реализация Unlambda на C#
  7. Домашняя страничка и документация SBT.
  8. Getting started with Scala using SBT