Haskell -> Python FFI

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
lunar

Hallo,

in meinem Blog nutze ich Pandoc, um aus Markdown HTML zu erzeugen. Pandoc ist so ziemlich der beste und umfangreichste Konverter für Markdown, mit Ausnahme des Syntax-Highlightings: Zwar unterstützt Pandoc Syntax-Highlighting, kommt aber im Bezug auf unterstützte Sprachen und Qualität nicht einmal annähernd an Pygments heran.

Dank der tollen Pandoc-API ist es nicht weiter schwierig, das Syntax-Highlighting durch Pygments zu ersetzen. Bisher habe ich einfach die offensichtliche Lösung verwendet, und einfach "pygmentize" mit dem Quelltext aufgerufen. Nicht weiter schwer, und schnell implementiert, allerdings auch sehr, sehr langsam.

Mithin habe ich mich am Wochenende mal daran gesetzt, Pygments direkt über Python's C-API heraus aufzurufen. Das ist dabei herausgekommen:
  • Foreign.Python.Native.hsc – Prototypen der entsprechenden C-Funktionen, und Deklarationen der C-Typen. Dank "hsc2hs" ist der GHC weitgehend selbstständig in der Lage, die richtigen C-Typen herauszufinden, so dass man sich nicht durch zig Header und Typedefs wühlen muss, um die “echten” Typen der CPython-API herauszufinden.
  • Foreign.Python – Der High-Level Wrapper um die C-API. Kann Module importieren, Attribute abfragen, Objekte aufrufen, und ein paar Haskell Typen zu Python-Typen hin und her konvertieren.
  • Text.Highlighting.Pygments – Der Pygments-Wrapper. Importiert die nötigen Module aus "pygments", und ruft "highlight" auf, um aus dem übergebenen Quelltext hübsches HTML zu erzeugen.
Das ganze war schlussendlich wesentlich einfacher als ich dachte, da Haskell's FFI viel Marshalling, vor allem aber die manuelle Speicherverwaltung, erspart. Dank "ForeignPtr" wird "Py_XDECREF" automatisch aufgerufen, sobald der entsprechende Haskell-Wrapper gelöscht wird. Mit "alloca" kann man automatisch temporäre Speicherbereiche für C erzeugen, die automatisch gelöscht werden, sobald sie nicht mehr benötigt werden.

Noch einfacher wäre es gewesen, wenn die C-API von Python 2 nicht so… bescheiden wäre. Insbesondere die ABI-Unterschiede zwischen UCS2 und UCS4 Builds machen die Angelegenheit komplizierter als nötig. Vielleicht reizt es mich ja noch mal irgendwann, dass ganze auf Python 3 zu portieren, dessen C-ABI ja einfacher sein soll…

Ich dachte, ich poste das hier einfach mal, falls es jemanden interessiert (und auch, weil ich ein ganz klein bisschen stolz darauf bin, wie weit meine eher bescheidenen Haskell-Kenntnisse dann doch tragen) :)
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Das sieht sehr interessant aus. Was mich allerdings etwas mehr interessieren würde ist, ob eigentlich die andere Richtung auch praktikabel ist, also von Python aus Haskell auszuführen. Ich denke dass könnte sehr interessant sein um z.B. Parsing in Haskell zu machen. Hast du dir sowas schonmal angeschaut?
lunar

Möglich wäre das schon, aber ob es sinnvoll ist, sei mal dahingestellt. Gute Parsing-Bibliotheken gibt es auch in Python…

Jedenfalls gibt es in Haskell das Gegenteil zu "foreign import", nämlich "foreign export“, mit dem man eine Haskell-Funktion mit einer C-ABI exportieren kann. Ein Haskell-Modul mit solchen Funktionen kann man dann mit ctypes laden. Gemacht habe ich das allerdings noch nie, und sehe so im Allgemeinen auch keinen Grund dazu.
Antworten