Gambit Scheme: интегрируемся с Python. Lisp.. Lisp. python.. Lisp. python. Scheme.. Lisp. python. Scheme. интероперабельность.. Lisp. python. Scheme. интероперабельность. Программирование.

Статья продолжает рассмотрение неочевидных возможностей Gambit Scheme, начатое в предыдущих статьях.

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

Постановка проблемы

По некоторым подсчётам, язык Python в настоящее время является наиболее популярным среди языков программирования. Не так важно, верно это или неверно в точности, но, во всяком случае, популярность Питона очень высока. Вместе с довольно хорошо продуманной питоновской модульной системой это привело к тому, что для этого языка разработано огромное количество практически полезных и часто хорошо отлаженных библиотек, количество которых в одном только стандартном репозитории PyPl оценивается более чем в 300 000. Это число намного превосходит те несколько тысяч библиотек, которые общедоступны для языка Scheme. В том числе, для Python доступны основные библиотеки нейросетевого искусственного интеллекта, что популярно и молодёжно, а в ряде случаев и приносит практическую пользу.

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

Возникает естественное желание совместить достоинства Scheme и Python в своих проектах. Эта задача решена в нескольких системах программирования, мы здесь рассмотрим её применение в Gambit Scheme, интегрируемой с CPython.

Инсталляция

Интеграция Gambit Scheme и Python недоступна в формате “целиком прямо из коробки”, но может быть достигнута небольшими усилиями. К сожалению, соответствующие средства достаточно слабо и фрагментарно документированы, что мы и постараемся восполнить настоящей статьёй.

Интеграция возможна в настоящий момент под управлением операционных систем Linux, macOS и Windows. Мы для определённости рассмотрим необходимые действия в Debian 12. Для других дистрибутивов Linux и для macOS последовательность действий в целом аналогична, для Windows потребуются небольшие уточнения, за которыми мы отошлём читателя к приведённым в конце статье источникам.

Общим ограничением является использование транслятора Python (CPython) версии не ниже 3.7.

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

  • Устанавливаем Debian 12.

  • Устанавливаем пакеты, которые нам понадобятся для сборки Gambit Scheme и интерфейса Scheme с Python:

sudo apt-get install gcc make python3-dev python3-pip python3.11-venv

Тут же можно установить и python3-tk, который мы будем использовать в примере.

  • Скачиваем исходные тексты Gambit Scheme с его сайта. Готовый пакет из дистрибутива ОС нам тут не подойдёт, так как придётся перекомпилировать в нестандартном режиме.

  • Распаковываем:

tar xvf gambit-v4_9_5.tgz
cd gambit-v4_9_5
  • Теперь производим конфигурирование и сборку с ключом --enable-multiple-threaded-vms, не используемым по умолчанию, но необходимым для взаимодействия с CPython:

./configure --enable-multiple-threaded-vms --enable-single-host --enable-march=native
make
sudo make install
sudo ln /usr/local/Gambit/bin/* /usr/local/bin/
  • Теперь у нас есть команды для вызова интерпретатора и компилятора gsi и gsc, что можно проверить, выдав их без параметров. Устанавливаем интерфейс к CPython:

gsi -install github.com/gambit/python
gsc github.com/gambit/python
  • Если мы используем Linux, то перед загрузкой Gambit Scheme необходимо руками подгружать динамическую библиотеку CPython. Это можно сделать, например, добавив в .bash_profile строчки (видоизменяемые в зависимости от конкретной версии Питона):

alias gsc='LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libpython3.11.so gsc'
alias gsi='LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libpython3.11.so gsi'
  • Всё! Теперь, если не возникло непредвиденных ошибок, всё должно работать.

Теперь, наконец, разберёмся, что же собственно и как должно работать.

Описание системы программирования

Gambit Scheme поддерживает практически свободное перемешивание кода Scheme и Python в программе (возьмём на себя смелость назвать такой код билингвальной программой, или просто билингвой). Для этого в операционной семантике билингвы используется вызов CPython через интерфейс динамической библиотеки, а в денотационной – несколько системных функций высокого уровня и специальный синтаксис SIX (Scheme Infix eXtension) Python.

Билингва может свободно использовать библиотеки для языка Python. Импортировать из PyPl в виртуальное окружение Python, используемое билингвой, проще всего функцией-обёрткой над PIP, pip-install. Чтобы работал следующий пример, выполним в gsi команды:

(pip-install "matplotlib")
(pip-install "networkx")

Можно было бы того же самого достичь через вызов pip3 из виртуального окружения:

.gambit_userlib/.venv3.11/bin/pip3 install matplotlib networkx

Сама билингва представляет собой обычный исходный файл .scm или .sld, содержащий в преамбуле импорт синтаксиса SIX Python и интерфейса к CPython:

(import (_six python) (github.com/gambit/python))

А после этого SIX позволяет нам перемешивать инфиксные операторы, написанные в синтаксисе Python, с обычными префиксными операторами Scheme.

Рассмотрим для примера конкретную билингву:

(import (_six python) (github.com/gambit/python))

import warnings
warnings.filterwarnings("ignore", category=UserWarning)
;; фильтр, чтобы matplotlib не ругался
;; на то, что он не в главной нитке
import matplotlib
matplotlib.use("TkAgg")
import networkx as nx
G=nx.Graph()

(define nodes '(a b c d e))
(define edges '((a b) (b c) (c d) (d e) (a d)))

G.add_nodes_from(`nodes)
G.add_edges_from(`edges)
nx.draw(G, with_labels=True, font_weight="bold")
matplotlib.pyplot.show()

Мы видим, что лексемы в инфиксном синтаксисе выделяются обратной косой чертой. Тут есть нюанс – черта сама по себе действует либо до окончания синтаксической конструкции (например, оператора importили внутренности каких-либо скобок), либо до пробельного символа. Поэтому в билингве, в отличие от Питона, имеет значение, что символ = в присваивании или левая скобка в вызове метода не отделяется пробелом.

Переменные Scheme и Python имеют разные лексические области видимости и разные типы. Типы переменных Python имеют внутреннее представление CPython и наследуются от PyObject, как им и положено положено. Поэтому, если мы напишем:

(define v 1)

то имеется в виду скимовская переменная v, которая получит значение 1 скимовского типа bignum, а если мы напишем:

v=2

то имеется в виду питоновская переменная v, которая получит значение 2 питоновского типа int:

> (pp v)

1

> print(v)

2

Однако, питоновский код может использовать скимовские переменные с соответствующим преобразованием типа через символ псевдоцитирования `:

> print(`v)

1

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

> (pp v)

2

Также можно взаимно вызывать и функции (которые и в Scheme, и в Python являются объектами первого класса).

Кроме того, вычислить значение строки, представляющей собой код на Питоне, в самом натуральном CPython можно при помощи функции python-eval:

> (python-eval "print('Hello')")

Hello

Аналогично же и питоновский код при необходимости может вычислять скимовские выражения через обычный скимовский eval:

> scheme_eval=`(lambda (s) (eval (call-with-input-string s read)))

> scheme_eval("(pp 'Hello)")

Hello

Таблица соответствия типов Scheme и Python представлена ниже:

Таблица соответствия типов

Таблица соответствия типов

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

Более глубоко о способе реализации связи между Gambit Scheme и CPython можно прочитать в источниках ниже.

Источники

A Foreign Function Interface between Gambit Scheme and CPython (статья)

Scheme 2021 – A lightweight approach for accessing Python modules from Gambit Scheme (видео)

gambit/python (проект на гитхабе и его readme)

Автор: vadimr

Источник

Рейтинг@Mail.ru
Rambler's Top100