使用Python调用Haskell函数

36

我想从Python中使用一些Haskell库(例如Darcs,Pandoc),但是似乎Python没有直接与Haskell进行外部函数接口的方法。有没有什么办法可以实现这个功能呢?


3
您可以始终通过串行化进行:https://github.com/nh2/call-haskell-from-anything。这很容易,但(可能)比通过 C 手动进行更慢。 - nh2
6个回答

22

如果您可以让Python代码调用C语言,那么您就可以通过FFI调用已导出的Haskell函数。

另一种方法是编写标准的IPC接口,在darcs和pandoc的情况下,只需将它们作为普通可执行文件调用并解析其输出即可。

关于自动化生成Haskell端无聊、重复的FFI和marshalling代码,我建议使用c2hs,它允许您根据现有的C接口自动生成很多代码。对于Python也可能有类似的工具。

遗憾的是,据我所知,SWIG从未被实现为Haskell,这可能是因为它面向的是类型不太严格的语言。


13
以下是有关ffi方法的更多详细说明:http://wiki.python.org/moin/PythonVsHaskell。 - sclv

12

另一个选项是连字号,可以在这里找到。基本用法看起来像这样:

>>> import hyphen, hs.Prelude
>>> hs.Prelude.sum([1,2,3]) # list converted to Haskell list
6
>>> hs.Prelude.drop(5, "Hello, world")
", world"
>>> hs.Prelude.drop(1, [1,2,3])
<hs.GHC.Types.[] object of Haskell type [GHC.Integer.Integer], containing '[2,3]'>
>>> list(hs.Prelude.drop(1, [1,2,3]))   # Convert back to Python list
[2, 3]

相比其他答案中的一些选项,这似乎不是一种轻量级的解决方案。

为了换取额外的重量,您似乎可以获得从Haskell到Python的完整桥梁。而 HaPygithub.com/nh2/call-haskell-from-anything 只允许您在Haskell函数的所有参数都来自相当基本的类型并且返回相当基本的类型时才能从Python中使用Haskell函数,hyphen 似乎可以让您使用 任意 函数。它之所以能够做到这一点,是因为它在Python中引入了一个表示Haskell堆上任意对象的类型。

这些“从Python视角看到的Haskell对象”表现得非常像Python对象。例如,Haskell的 Map 表现得有点像字典:

>>> import hs.Data.Map
>>> my_map = hs.Data.Map.fromList([(1, 'Hello'), (2, 'World')])
>>> my_map[1]
'Hello'
>>> print(sorted([key for key in my_map]))
[1, 2]

请查看自述文件获取更多示例!

它似乎还可以处理各种花哨的事情,如在Haskell和Python之间转换异常。


11

另外一个想法:比直接绑定C但比通过外壳调用Haskell更高效的是rpc系统,例如Apache Thrift:http://incubator.apache.org/thrift/

我发现thrift易于使用、得到良好支持并具有合理的性能。一旦你运行了Haskell服务器,本地通信的成本相当便宜,尽管与直接使用c类型相比,在编组/解组方面付出了更高的代价。

还有至少两个用于从Haskell调用Python的包,missingpy(http://hackage.haskell.org/package/MissingPy)和cpython(http://hackage.haskell.org/package/cpython)。后者声称正在计划支持反向调用--尽管您必须询问作者,是否仍然是这种情况,如果是,何时可用。


@sciv 你在 Apache Thrift 中找到了任何外部函数调用的示例吗?我在这里遇到了同样的问题。 - Anderson Green
2
什么是 ffi?Thrift 是一个序列化和客户端/服务器库。 - sclv

8

我是一名新手。

但是,我成功地使用Haskell的FFI从Python中调用了用户定义的Haskell函数。 基本上,我将Haskell函数编译为dll文件,并在Python中使用ctypes导入了该dll文件。 因此,这个函数在Python中变得可用。

我在这里写了具体操作过程:https://justa0xc0de.wordpress.com/2015/01/08/using_haskell_function_in_python/

希望这有所帮助。


1
这篇文章看起来非常有用,但你应该提取一些关键思想并将它们复制到你的答案中。否则,它可能会被删除为“仅链接”。 - dfeuer

5

这里有一个包装器,允许从Python中调用Haskell函数:

https://github.com/sakana/HaPy

初步检查它似乎要求Haskell函数具有相对简单的类型签名(基本上,所有涉及的类型最好是像Int和Float这样的c语言所知道的类型,或者是这种形式的事物的列表、列表的列表等)。

提供了一个示例,其中有这个Haskell代码:

module ExampleModule where

import Data.Char

foo :: Double -> Double -> Double
foo = (*)

bar :: Int -> Int
bar i = sum [1..i]

baz :: Int -> Bool
baz = (> 5)

arr_arg :: [Int] -> Int
arr_arg = sum

arr_ret :: Int -> [Int]
arr_ret i = [1..i]

arr_complex :: [[Int]] -> [[Int]]
arr_complex = map (map (* 2))

string_fun :: String -> String
string_fun str = str ++ reverse str

char_test :: Char -> Int
char_test = ord

然后可以像这样访问它:

from HaPy import ExampleModule

print "3 * 7 is", ExampleModule.foo(3,7)
print "sum from 1 to 10 is", ExampleModule.bar(10)
print "3 > 5 is", ExampleModule.baz(3)

print "sum from 1 to 100 is", ExampleModule.arr_arg(range(101))
print "numbers from 1 to 10 are", ExampleModule.arr_ret(10)

print "complex array passing:", ExampleModule.arr_complex([range(3), [], range(100)])
print "string fun:", ExampleModule.string_fun("This isn't really a palindrome.")

s = ExampleModule.string_fun("abc\000def")
print "string fun with nulls:", s,
for c in s:
    print ord(c),
print

print "char test:", ExampleModule.char_test("t")

不幸的是,您需要在Haskell端进行一些导出管道工作。


4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接