Gambit Scheme: интегрируемся с Python

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

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

Источник: https://habr.com/ru/articles/884654/


Интересные статьи

Интересные статьи

Количество разработчиков из Индии и Азии растет год от года, а активное внедрение ИИ может сделать базовую разработку более дешевой. Автор журнала The Economist рассуждает, как будет меняться мир при ...
Статья направлена на решение проблемы "зависания" выгрузки результатов опросов при использовании модуля Vote 1С-Битрикс в случае, если в опросе много вопросов или ответов. Так-же в статье реализована ...
В предыдущей статье я вкратце упомянул, что Knime умеет работать с базами данных, в том числе с NoSQL базой MongoDB. На мой взгляд, MongoDB является довольно простым и эффективным решением для хранени...
Мне было необходимо делать 2 раза в сутки бэкап сайта на «1С-Битрикс: Управление сайтом» (файлов и базы mysql) и хранить историю изменений за 90 дней. Сайт расположен на VDS под уп...
Есть статьи о недостатках Битрикса, которые написаны программистами. Недостатки, описанные в них рядовому пользователю безразличны, ведь он не собирается ничего программировать.