cl4py - Common Lisp for Python
==============================
The library cl4py (pronounce as *clappy*) allows Python programs to call
Common Lisp libraries. Its official mascot is the cl4py-bird:
.. image:: ./cl4py.png
Motivation
----------
You are a Python programmer, but you want access to some of the powerful
features of Lisp, for example to compile code at run time? Or you want to
use some `awesome Lisp libraries <http://codys.club/awesome-cl/>`_? Or
you are a Lisp programmer and want to show your work to your Python
friends. In all these cases, cl4py is here to help you.
Tutorial
--------
You can start any number of Lisp subprocesses within Python, like this:
.. code:: python
>>> import cl4py
>>> lisp = cl4py.Lisp()
Of course, this requires you have some Lisp installed. If not, use
something like ``apt install sbcl``, ``pacman -S sbcl`` or ``brew install
sbcl`` to correct this deficiency. Once you have a running Lisp process,
you can execute Lisp code on it:
.. code:: python
# In Lisp, numbers evaluate to themselves.
>>> lisp.eval( 42 )
42
# ('+', 2, 3) is a short notation for cl4py.List(cl4py.Symbol('+'), 2, 3).
# For convenience, whenever a Python tuple is converted to Lisp
# data, any strings therein are automatically converted to Lisp symbols.
>>> lisp.eval( ('+', 2, 3) )
5
# Nested expressions are allowed, too.
>>> lisp.eval( ('/', ('*', 3, 5), 2) )
Fraction(15, 2)
# Use cl4py.List instead of tuples to avoid the automatic conversion of
# strings to symbols.
>>> lisp.eval( cl4py.List(cl4py.Symbol('STRING='), 'foo', 'bar') )
()
>>> lisp.eval( cl4py.List(cl4py.Symbol('STRING='), 'foo', 'foo') )
True
# Here is how you can lookup a symbol's value:
>>> lisp.eval(cl4py.Symbol('*PRINT-BASE*', 'COMMON-LISP'))
10
# Of course you can also use Lisp macros:
>>> lisp.eval( ('loop', 'for', 'i', 'below', 5, 'collect', 'i') )
List(0, 1, 2, 3, 4)
>>> lisp.eval( ('with-output-to-string', ('stream',),
('princ', 12, 'stream'),
('princ', 34, 'stream')) )
'1234'
A cl4py.Lisp object not only provides ``eval``, but also methods for
looking up functions and packages:
.. code:: python
>>> add = lisp.function('+')
>>> add(1, 2, 3, 4)
10
>>> div = lisp.function('/')
>>> div(2, 4)
Fraction(1, 2)
# Lisp packages are automatically converted to Python modules.
>>> cl = lisp.find_package('CL')
>>> cl.oddp(5)
True
>>> cl.cons(5, None)
List(5)
>>> cl.remove(5, [1, -5, 2, 7, 5, 9], key=cl.abs)
[1, 2, 7, 9]
# Higher-order functions work, too!
>>> cl.mapcar(cl.constantly(4), (1, 2, 3))
List(4, 4, 4)
# cl4py even supports macros and special forms as a thin
# wrapper around lisp.eval.
>>> cl.loop('repeat', 5, 'collect', 42)
List(42, 42, 42, 42, 42)
>>> cl.progn(5, 6, 7, ('+', 4, 4))
8
When converting Common Lisp packages to Python modules, we run into the
problem that not every Common Lisp symbol name is a valid Python
identifier. As a remedy, so we attempt to substitute problematic
characters and symbols with something that Python can digest. Here you can
see this substitution rules in action:
.. code:: python
# hyphens are turned into underscores
>>> cl.type_of("foo")
List(Symbol("SIMPLE-ARRAY", "COMMON-LISP"), Symbol("CHARACTER", "COMMON-LISP"), List(3))
# The functions +, -, *, /, 1+, and 1- are renamed to add, sub,
# mul, div, inc, and dec, respectively.
>>> cl.add(2,3,4,5)
14
# Within a string, occurrences of -, *, +, <=, <, =, /=, >=, gt, and ~,
# are replaced by _, O, X, le, lt, sim, ne, ge, ge, gt, and tilde, respectively.
>>> cl.stringgt('baz', 'bar')
2
# Earmuffs are stripped
>>> cl.print_base
10
# Constants are capitalized
>>> cl.MOST_POSITIVE_DOUBLE_FLOAT
1.7976931348623157e+308
The cl4py module provides a Cons class that mimics cons cells in Lisp.
.. code:: python
>>> lisp.eval( ('CONS', 1, 2) )
Cons(1, 2)
>>> lst = lisp.eval( ('CONS', 1, ('CONS', 2, () )) )
List(1, 2)
>>> lst.car
1
>>> lst.cdr
List(2) # an abbreviation for Cons(2, ())
# cl4py Conses are iterable!
>>> list(lst)
[1, 2]
>>> sum(lst)
3
# cl4py also supports dotted and circular lists.
>>> lisp.eval( ('CONS', 1, ('CONS', 2, 3 )) )
DottedList(1, 2, 3)
>>> twos = cl.cons(2,2)
>>> twos.cdr = twos
>>> twos
DottedList(2, ...)
>>> cl.mapcar(lisp.function('+'), (1, 2, 3, 4), twos)
List(3, 4, 5, 6)
Frequently Asked Problems
-------------------------
Why does my Lisp subprocess complain about ``Package QL does not exist``.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, cl4py starts a Lisp subprocess with ``sbcl --script``. This
means, that the Lisp process will ignore any user initialization files,
including the Quicklisp setup. However, we provide an extra option for
installing and loading Quicklisp automatically: ``quicklisp=True``
.. code:: python
>>> lisp = cl4py.Lisp(quicklisp=True);
>>> ql = lisp.find_package('QL')
>>> ql.quickload('YOUR-SYSTEM')
Related Projects
----------------
- `burgled-batteries <https://github.com/pinterface/burgled-batteries>`_
- A bridge between Python and Lisp. The goal is that Lisp programs can
use Python libraries, which is in some sense the opposite of
cl4py. Furthermore it relies on the less portable mechanism of FFI
calls.
- `CLAUDE <https://www.nicklevine.org/claude/>`_
- An earlier attempt to access Lisp libraries from Python. The key
difference is that cl4py does not run Lisp directly in the host
process. This makes cl4py more portable, but complicates the exchange of
data.
- `cl-python <https://github.com/metawilm/cl-python>`_
- A much heavier solution than cl4py --- let's simply implement Python
in Lisp! An amazing project. However, cl-python cannot access foreign
libraries, e.g., NumPy. And people are probably hesitant to migrate away
from CPython.
- `Hy <http://docs.hylang.org/en/stable/>`_
- Python, but with Lisp syntax. This project is certainly a great way to
get started with Lisp. It allows you to study the advantages of Lisp's
seemingly weird syntax, without leaving the comfortable Python
ecosystem. Once you understand the advantages of Lisp, you will doubly
appreciate cl4py for your projects.
- `py4cl <https://github.com/bendudson/py4cl>`_
- A library that allows Common Lisp code to access Python libraries. It
is basically the inverse of cl4py.
Raw data
{
"_id": null,
"home_page": "https://github.com/marcoheisig/cl4py",
"name": "cl4py",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "foreign functions FFI",
"author": "Marco Heisig",
"author_email": "marco.heisig@fau.de",
"download_url": "https://files.pythonhosted.org/packages/6c/5f/8018862022f66c88676c8a1f21c26bb3cc6a6a6e9a77f878cd13bbb292d5/cl4py-1.8.2.tar.gz",
"platform": null,
"description": "cl4py - Common Lisp for Python\n==============================\n\nThe library cl4py (pronounce as *clappy*) allows Python programs to call\nCommon Lisp libraries. Its official mascot is the cl4py-bird:\n\n.. image:: ./cl4py.png\n\nMotivation\n----------\n\nYou are a Python programmer, but you want access to some of the powerful\nfeatures of Lisp, for example to compile code at run time? Or you want to\nuse some `awesome Lisp libraries <http://codys.club/awesome-cl/>`_? Or\nyou are a Lisp programmer and want to show your work to your Python\nfriends. In all these cases, cl4py is here to help you.\n\nTutorial\n--------\n\nYou can start any number of Lisp subprocesses within Python, like this:\n\n.. code:: python\n\n >>> import cl4py\n >>> lisp = cl4py.Lisp()\n\nOf course, this requires you have some Lisp installed. If not, use\nsomething like ``apt install sbcl``, ``pacman -S sbcl`` or ``brew install\nsbcl`` to correct this deficiency. Once you have a running Lisp process,\nyou can execute Lisp code on it:\n\n.. code:: python\n\n # In Lisp, numbers evaluate to themselves.\n >>> lisp.eval( 42 )\n 42\n\n # ('+', 2, 3) is a short notation for cl4py.List(cl4py.Symbol('+'), 2, 3).\n # For convenience, whenever a Python tuple is converted to Lisp\n # data, any strings therein are automatically converted to Lisp symbols.\n >>> lisp.eval( ('+', 2, 3) )\n 5\n\n # Nested expressions are allowed, too.\n >>> lisp.eval( ('/', ('*', 3, 5), 2) )\n Fraction(15, 2)\n\n # Use cl4py.List instead of tuples to avoid the automatic conversion of\n # strings to symbols.\n >>> lisp.eval( cl4py.List(cl4py.Symbol('STRING='), 'foo', 'bar') )\n ()\n >>> lisp.eval( cl4py.List(cl4py.Symbol('STRING='), 'foo', 'foo') )\n True\n\n # Here is how you can lookup a symbol's value:\n >>> lisp.eval(cl4py.Symbol('*PRINT-BASE*', 'COMMON-LISP'))\n 10\n\n # Of course you can also use Lisp macros:\n >>> lisp.eval( ('loop', 'for', 'i', 'below', 5, 'collect', 'i') )\n List(0, 1, 2, 3, 4)\n\n >>> lisp.eval( ('with-output-to-string', ('stream',),\n ('princ', 12, 'stream'),\n ('princ', 34, 'stream')) )\n '1234'\n\nA cl4py.Lisp object not only provides ``eval``, but also methods for\nlooking up functions and packages:\n\n.. code:: python\n\n >>> add = lisp.function('+')\n >>> add(1, 2, 3, 4)\n 10\n\n >>> div = lisp.function('/')\n >>> div(2, 4)\n Fraction(1, 2)\n\n # Lisp packages are automatically converted to Python modules.\n >>> cl = lisp.find_package('CL')\n >>> cl.oddp(5)\n True\n\n >>> cl.cons(5, None)\n List(5)\n\n >>> cl.remove(5, [1, -5, 2, 7, 5, 9], key=cl.abs)\n [1, 2, 7, 9]\n\n # Higher-order functions work, too!\n >>> cl.mapcar(cl.constantly(4), (1, 2, 3))\n List(4, 4, 4)\n\n # cl4py even supports macros and special forms as a thin\n # wrapper around lisp.eval.\n >>> cl.loop('repeat', 5, 'collect', 42)\n List(42, 42, 42, 42, 42)\n\n >>> cl.progn(5, 6, 7, ('+', 4, 4))\n 8\n\nWhen converting Common Lisp packages to Python modules, we run into the\nproblem that not every Common Lisp symbol name is a valid Python\nidentifier. As a remedy, so we attempt to substitute problematic\ncharacters and symbols with something that Python can digest. Here you can\nsee this substitution rules in action:\n\n.. code:: python\n\n # hyphens are turned into underscores\n >>> cl.type_of(\"foo\")\n List(Symbol(\"SIMPLE-ARRAY\", \"COMMON-LISP\"), Symbol(\"CHARACTER\", \"COMMON-LISP\"), List(3))\n\n # The functions +, -, *, /, 1+, and 1- are renamed to add, sub,\n # mul, div, inc, and dec, respectively.\n >>> cl.add(2,3,4,5)\n 14\n\n # Within a string, occurrences of -, *, +, <=, <, =, /=, >=, gt, and ~,\n # are replaced by _, O, X, le, lt, sim, ne, ge, ge, gt, and tilde, respectively.\n >>> cl.stringgt('baz', 'bar')\n 2\n\n # Earmuffs are stripped\n >>> cl.print_base\n 10\n\n # Constants are capitalized\n >>> cl.MOST_POSITIVE_DOUBLE_FLOAT\n 1.7976931348623157e+308\n\nThe cl4py module provides a Cons class that mimics cons cells in Lisp.\n\n.. code:: python\n\n >>> lisp.eval( ('CONS', 1, 2) )\n Cons(1, 2)\n\n >>> lst = lisp.eval( ('CONS', 1, ('CONS', 2, () )) )\n List(1, 2)\n >>> lst.car\n 1\n >>> lst.cdr\n List(2) # an abbreviation for Cons(2, ())\n\n # cl4py Conses are iterable!\n >>> list(lst)\n [1, 2]\n >>> sum(lst)\n 3\n\n # cl4py also supports dotted and circular lists.\n >>> lisp.eval( ('CONS', 1, ('CONS', 2, 3 )) )\n DottedList(1, 2, 3)\n\n >>> twos = cl.cons(2,2)\n >>> twos.cdr = twos\n >>> twos\n DottedList(2, ...)\n\n >>> cl.mapcar(lisp.function('+'), (1, 2, 3, 4), twos)\n List(3, 4, 5, 6)\n\n\nFrequently Asked Problems\n-------------------------\n\nWhy does my Lisp subprocess complain about ``Package QL does not exist``.\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBy default, cl4py starts a Lisp subprocess with ``sbcl --script``. This\nmeans, that the Lisp process will ignore any user initialization files,\nincluding the Quicklisp setup. However, we provide an extra option for\ninstalling and loading Quicklisp automatically: ``quicklisp=True``\n\n\n.. code:: python\n\n >>> lisp = cl4py.Lisp(quicklisp=True);\n >>> ql = lisp.find_package('QL')\n >>> ql.quickload('YOUR-SYSTEM')\n\n\nRelated Projects\n----------------\n\n- `burgled-batteries <https://github.com/pinterface/burgled-batteries>`_\n - A bridge between Python and Lisp. The goal is that Lisp programs can\n use Python libraries, which is in some sense the opposite of\n cl4py. Furthermore it relies on the less portable mechanism of FFI\n calls.\n- `CLAUDE <https://www.nicklevine.org/claude/>`_\n - An earlier attempt to access Lisp libraries from Python. The key\n difference is that cl4py does not run Lisp directly in the host\n process. This makes cl4py more portable, but complicates the exchange of\n data.\n- `cl-python <https://github.com/metawilm/cl-python>`_\n - A much heavier solution than cl4py --- let's simply implement Python\n in Lisp! An amazing project. However, cl-python cannot access foreign\n libraries, e.g., NumPy. And people are probably hesitant to migrate away\n from CPython.\n- `Hy <http://docs.hylang.org/en/stable/>`_\n - Python, but with Lisp syntax. This project is certainly a great way to\n get started with Lisp. It allows you to study the advantages of Lisp's\n seemingly weird syntax, without leaving the comfortable Python\n ecosystem. Once you understand the advantages of Lisp, you will doubly\n appreciate cl4py for your projects.\n- `py4cl <https://github.com/bendudson/py4cl>`_\n - A library that allows Common Lisp code to access Python libraries. It\n is basically the inverse of cl4py.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Common Lisp for Python",
"version": "1.8.2",
"project_urls": {
"Homepage": "https://github.com/marcoheisig/cl4py"
},
"split_keywords": [
"foreign",
"functions",
"ffi"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "653083f1f82dbabfad0fb66b1437c61abd478440a3c5f717be376d5940cb9cca",
"md5": "ad8aaecf6ffec6389f907c9bf2ce47de",
"sha256": "9c5e6797b374647b249d730ee8c44ee7e78aa9101aa0fd90529a4e427e539439"
},
"downloads": -1,
"filename": "cl4py-1.8.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "ad8aaecf6ffec6389f907c9bf2ce47de",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 24114,
"upload_time": "2024-12-16T14:03:20",
"upload_time_iso_8601": "2024-12-16T14:03:20.697478Z",
"url": "https://files.pythonhosted.org/packages/65/30/83f1f82dbabfad0fb66b1437c61abd478440a3c5f717be376d5940cb9cca/cl4py-1.8.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6c5f8018862022f66c88676c8a1f21c26bb3cc6a6a6e9a77f878cd13bbb292d5",
"md5": "086520b138d4a3f8a85be506e6c3ad9a",
"sha256": "e202c6bab24e1b5390e37e52c897c0186251f1a341bfb1a04b05b58ffb791d90"
},
"downloads": -1,
"filename": "cl4py-1.8.2.tar.gz",
"has_sig": false,
"md5_digest": "086520b138d4a3f8a85be506e6c3ad9a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 27195,
"upload_time": "2024-12-16T14:03:22",
"upload_time_iso_8601": "2024-12-16T14:03:22.336238Z",
"url": "https://files.pythonhosted.org/packages/6c/5f/8018862022f66c88676c8a1f21c26bb3cc6a6a6e9a77f878cd13bbb292d5/cl4py-1.8.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-16 14:03:22",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "marcoheisig",
"github_project": "cl4py",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"lcname": "cl4py"
}