指向Haskell中的ADT

时间:2013-12-18 09:03:33

标签: pointers haskell ffi

我想在Haskell中实现术语图,这样我就可以实现一个使用共享的术语重写引擎。像

这样的东西
data TG f v = Var v | Op f [TG f v] | P (Ptr (TG f v))

我希望以下内容有意义:

let
    t' = Op 'f' [Var 'x', Var 'y']
    t = getPointer t'
in
    Op 'g' [P t,P t]

然后在重写期间,我只需要重写一次。

然而,我注意到两件事:(1)模块被称为Foreign.Storable,因此它应该仅用于FFI的东西,(2)对于任何类型的列表,没有Foreign.Storable的实例;这是为什么?

1 个答案:

答案 0 :(得分:1)

正如评论中所指出的,如果你想在Haskell中定义一个普通的代数数据类型但是可以访问图结构,你需要使用observable sharing的一些变体。类似ForeignPtr的类型实际上是与外部代码或低级内存管理接口,并不适合这种情况。

可观察共享的所有可用技术都需要某种轻微的“不安全”和#34;代码 - 因为负担是用户不要滥用它。问题是Haskell的语义并不是为了让你能够看到""两个值是否是相同的指针。但实际上,最糟糕的情况是,您会错过用户使用单一定义的某些情况,因此您最终会在内部数据结构中出现重复。根据您自己的结构的语义,这可能只会对性能产生影响。

可观察共享通常基于pointer equality的较低级别原语 - 即检查两个指定的Haskell值是否实际存储在内存中的完全相同的位置,或更通用的stable names,它们代表内存中单个Haskell值的位置,可以存储在表中,稍后进行比较。

data-reify这样的高级库有助于隐藏这些细节。

使用可观察共享的最好方法是允许用户编写代数类型的正常值,例如:仅举个例子:

let t = Op 'f' [Var 'x', Var 'y']
in Op 'g' [P t,P t]

然后让您的库使用可观察共享的任何方法,一旦您收到用户的值,就将其转换为某种显式图形结构。例如,您可能会使用显式指针转换为不同的数据类型,或者使用它们扩充TG类型。显式指针只是对您自己的地图结构的某种查找,例如

data InternalTG f v = ... | Pointer Int
type TGMap f v = IntMap (InternalTG f v)

如果使用data-reify之类的内容,那么InternalTG f v将成为DeRef的{​​{1}}类型。

然后,您可以对生成的图形结构进行重写。

作为使用可观察共享的替代方法,如果您愿意让您的用户使用monad构建其值并明确选择何时使用共享(如上面包含TG f v所示),那么你可以简单地使用状态monad来明确地构建图形:

getPointer

一旦你有了任何路线的图形,有各种各样的操作技术,但这些可能超出了原始问题的范围。