如何在Haskell中模拟对象身份的概念
我正在考虑用Haskell设计一个类似Python的面向对象语言的解释器。现在我遇到的一个问题是关于对象身份的概念。如果我们看看Python的id(object)
函数,它的定义是返回一个对象的“身份”。这个身份是一个整数(或者长整数),在对象的生命周期内是唯一且不变的。也就是说,两个生命周期不重叠的对象可能会有相同的id()值。(实现说明:这个身份实际上是对象的地址。)
那么,在Haskell中实现这样的概念一般应该怎么做呢?
2 个回答
这个问题的答案取决于你打算如何实现那些你想要获取ID的对象。如果两个变量包含相同的对象,那它们看起来会是什么样子呢?你基本上需要在变量中存储对“可变”对象的引用,关键在于你到底是怎么做到这一点的。如果你只是把简单的值直接赋给变量名,那么一个变量的变化就不会在另一个变量中反映出来,这样就没有对象的身份了。
所以,变量需要保存对对象当前值的引用。可以这样理解:
data VariableContent = Int | String | ObjRef Int | ...
data ObjStore = ObjStore [(Int, Object)]
data ProgramState = ProgramState ObjStore VariableStore ...
在这里,每个 ObjRef
都指向 ObjStore
中的一个值,这个值可以通过存储在 ObjRef
中的 Int
ID 来访问。而这个 Int
就是 id(object)
函数应该返回的内容。
总的来说,id
函数是非常依赖于你如何实际实现对象引用的。
我假设你的解释器会在一个叫做 State
的状态管理中运行。这个状态可能包含一些活跃的对象和其他相关信息。你可以做的是,跟踪一个可用的(未使用的)id
列表(用 Int
表示),并给每个 Object
加上一个 Int
,也就是它的 id
。这个 id
是从可用的 id
列表中取出来的,并在创建时分配给它。因此,不会有两个 Object
实例拥有相同的 id
。
注意我提到的是 Int
。你可以选择 Integer
,但这样可能效率会降低。使用 Int
的话,你需要在对象被销毁时,把它的 id
重新放回可用的 id
列表中(否则你最终会用完可用的 id
)。所以我想象的情况是这样的:
data Object a = Obj Int a
instance Eq (Object a) where
Obj i _ == Obj j _ = i == j
type InterpreterState a = State [Int] a
createObject :: a -> InterpreterState (Object a)
createObject a = do
(i:is) <- get
put is
return $ Obj i a
destroyObject :: Object a -> InterpreterState ()
destroyObject (Obj i a) = do
modify (i:)
(注意,InterpreterState
在你的情况下会复杂得多,而 createObject
和 destroyObject
应该会把对象添加到或从状态中移除。但这不是重点。)