MArray类提供了通用函数,用于在ST和IO上下文中处理各种类型的可变数组。我无法找到一个类似的类来处理STRefs和IORefs。这样的事情存在吗?
答案 0 :(得分:6)
ref-fd
包提供了它:
class Monad m => MonadRef r m | m -> r where
[...]
或类型系列ref-tf
:
class Monad m => MonadRef m where
type Ref m :: * -> *
[...]
另一个答案表明monad-statevar包没有功能依赖。它还具有单独的HasGet
和HasPut
成员,并且没有newRef
功能的抽象。
除了每种方法中的不同方法外,功能依赖性是一种设计权衡。考虑以下两个简化类:
class MRef1 r m where
newRef1 :: a -> m (r a)
readRef1 :: r a -> m a
class MRef2 r m | m -> r where
newRef2 :: a -> m (r a)
readRef2 :: r a -> m a
使用MRef1
时,monad类型和引用类型都可以自由变化,因此以下代码具有类型错误:
useMRef1 :: ST s Int
useMRef1 = do
r <- newRef1 5
readRef1 r
No instance for (MRef1 r0 (ST s)) arising from a use of `newRef1'
The type variable `r0' is ambiguous
我们必须在某处添加额外的类型签名,以表示我们要使用STRef
。
相比之下,相同的代码适用于MRef2
而没有任何额外的签名。定义上的签名表示整个代码具有类型ST s Int
,并结合功能依赖m -> r
意味着给定r
类型只有一个m
类型,所以编译器知道我们现有的实例是唯一可能的实例,我们必须要使用STRef
。
另一方面,假设我们想要一种新的参考,例如STRefHistory
跟踪所有已存储在其中的值:
newtype STRefHistory s a = STRefHistory (STRef s [a])
MRef1
实例很好,因为我们允许多个引用类型用于相同的monad类型:
instance MRef1 (STRefHistory s) (ST s) where
newRef1 a = STRefHistory <$> newSTRef [a]
readRef1 (STRefHistory r) = head <$> readSTRef r
但是等效的MRef2
实例失败了:
Functional dependencies conflict between instance declarations:
instance MRef2 (STRef s) (ST s) -- Defined at mref.hs:28:10
instance MRef2 (STRefHistory s) (ST s) -- Defined at mref.hs:43:10
我还提到了类型系列版本,它在功能依赖性方面表现力非常相似;引用类型是monad类型的“类型函数”,因此每个monad只能有一个。语法最终有点不同,特别是你可以在约束中说MonadRef m
,而不说明约束中的引用类型。
具有相反的功能依赖性也是合理的:
class MRef2 r m | r -> m where
这样每个引用类型只能存在一个monad中,但是你仍然可以为monad提供多种引用类型。那么你需要在引用上输入类型签名,而不是整体上的monadic计算。
答案 1 :(得分:5)
Control.Monad.StateVar有一个类型类,可以让get
和put
IORef和STRef完全相同。