Статья продолжает рассмотрение неочевидных возможностей 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 (видео)
Автор: vadimr