我正在研究与MonadRef
相关的this problem。看一下定义
class MonadRef r m | m -> r where
newRef :: a -> m (r a)
readRef :: r a -> m a
writeRef :: r a -> a -> m ()
我在考虑如何使用纯数据结构实现此功能但未能找到答案。实际上,所有已知的独立实现(不依赖于另一个MonadRef
)
instance TVar STM
instance IORef IO
instance (STRef s) (ST s)
要求RealWorld
进行实践。这是否意味着我们只能拥有不纯粹的MonadRef
?
当我尝试解决此问题时,我首先发现Maybe
无法实现MonadRef
只是因为它太简单而且没有空间来记录所需信息。作为一般概括,我得出结论,对于任何实现,Monad
必须能够包含任意数量的信息,因为您可以尽可能多地调用newRef
。
所以我考虑了[]
,但它仍然是失败的,因为存储在Monad中的数据可以是任何类型。我不知道如何在Haskell中构建一个可以存储任何类型数据的容器,同时仍然能够以适当的类型提取数据(也许existential quantification可以帮助吗?但我仍然没有&#39 ;我知道怎么做)。
答案 0 :(得分:3)
你可以几乎到达那里(纯粹实现ST
的等价物),使用State
monad,其状态是来自引用的Map
字典ID为该引用内的值。
但是Haskell的类型系统缺少依赖类型,其中值的类型取决于另一个固定类型术语的值。因此,它无法表达引用中的类型取决于引用的ID,以允许所有类型。
您可以通过让存储的值为GHC.Exts.Any
类型并使用来解决此问题
Unsafe.Coerce.unsafeCoerce
在内部转换为正确的类型。
但正如所宣传的那样unsafeCoerce
是不安全的。这再次意味着Haskell的类型系统无法保证您以类型安全的方式使用引用,如果您出错,您可以通过这种方式使GHC崩溃。 (如果MonadRef
对其参考内容有Data.Typeable.Typeable
约束,可能已经习惯了这样做,但事实并非如此。(然后你就可以放Data.Dynamic.Dynamic
代替地图内Any
。))
但是,如果 以这种方式使用unsafeCoerce
,请注意隐藏所使用的ID类型,并实现与ST
相同的API,包括s
参数可以防止不同的ST
“线程”混淆彼此的引用,那么这实际上应该可以安全地作为库工作。我找到了at least one attempt on GitHub。 (它也试图作为变压器工作,虽然我怀疑这是否安全。)