Pointers Haskell中ADT的指针

Pointers Haskell中ADT的指针,pointers,haskell,ffi,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] 那么在重写的过程中,我只需要重写t一次 然而,我注意到了两件事:(1)该模块被称为Foreign.S

我想在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]
那么在重写的过程中,我只需要重写t一次


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

正如评论中指出的,如果您想在Haskell中定义一个普通的代数数据类型,但要访问图形结构,则需要使用的一些变量。像
ForeignPtr
这样的类型实际上是用于与外部代码或低级内存管理接口的,不适合这种情况

所有可用的可观察共享技术都需要某种稍微“不安全”的代码,因为不滥用代码的责任在于用户。问题是Haskell的语义不允许您“查看”两个值是否为同一指针。然而,在实践中,可能发生的最糟糕情况是,您将错过某些用户使用单一定义的情况,因此最终会导致内部数据结构中出现重复。根据您自己结构的语义,这可能会对性能产生影响

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

更高级别的库,如帮助,可以对您隐藏这些详细信息

使用可观察共享的最好方法是允许用户编写代数类型的正常值,例如,对于您的示例,只需:

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
将是
TG f v
DeRef
类型

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

作为使用可观察共享的替代方案,如果您愿意让用户使用monad来构建他们的值并明确选择何时使用共享(如上面包含的
getPointer
所建议的),那么您可以简单地使用state monad显式构建图形:

-- your code
data TGState f v = TGState { tgMap :: IntMap (TG f v), tgNextSymbol :: Int }

initialTGState :: TGState f v
initialTGState = TGState { tgMap = IntMap.empty, tgNextSymbol = 0 }

type TGMonad f v a = State (TGState f v) a

newtype Ptr tg = Ptr Int -- a "phantom type" just to give some type safety

getPointer :: TG f v -> TGMonad f v (Ptr (TG f v))
getPointer tg = do
   tgState <- get
   let sym = tgNextSymbol tgState
   put $
       TGState { tgMap = IntMap.insert sym tg (tgMap tgState),
                 tgNextSymbol = sym + 1 }
   return (Ptr sym)

runTGMonad :: TGMonad a -> (a, IntMap (TG f v))
runTGMonad m =
    let (v, tgState) = runState m
    (v, tgMap tgState)

-- user code

do
    let t' = Op 'f' [Var 'x', Var 'y']
    t <- getPointer t'
    return $ Op 'g' [P t,P t]
——您的代码
数据TGState tgf v=TGState{tgMap::IntMap(tgf v),tgNextSymbol::Int}
initialTGState::TGState f v
initialTGState=TGState{tgMap=IntMap.empty,tgNextSymbol=0}
类型TGMonad f v a=状态(TGState f v)a
newtype Ptr tg=Ptr Int——一种“幻影类型”,只是为了提供某种类型的安全性
getPointer::TG f v->TGMonad f v(Ptr(TG f v))
getPointer tg=do
tgState(a,IntMap(TG f v))
朗特格莫纳德m=
设(v,tgState)=运行状态m
(五、tgMap tgState)
--用户代码
做
设t'=Op'f'[Var'x',Var'y']

请不要按照中的“可观察共享”来概述几种方法。在这种情况下,您可能会觉得很方便。您不需要指针。对于可更新的引用,您可能需要
STRef
IORef
。或者你根本不需要更新。如果不知道你想做什么,很难知道。@d8d0d65b3f7cf42:谢谢你的链接。Gill纸似乎可以直接使用,而Classen纸需要对Haskell进行非保守的扩展,如Lava语言所示。你知道这两种方法是否都有直接的Haskell实现吗?@chunksOf50:类似的方法可能会奏效。我将不得不跟踪地图或一些有效数据结构中的多个替换。你是这么想的吗?会有一些效率损失,但在实践中可能可以忽略不计?