我想在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的实例;这是为什么?
答案 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
一旦你有了任何路线的图形,有各种各样的操作技术,但这些可能超出了原始问题的范围。