当我尝试编译这个时:
module Main where
import qualified Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST
myRead mv = runST $ MV.read mv 0
我收到以下错误消息:
Could not deduce (t ~ U.MVector s a)
from the context (U.Unbox a)
bound by the inferred type of myRead :: U.Unbox a => t -> a
at src/Main.hs:53:1-32
`t' is a rigid type variable bound by
the inferred type of myRead :: U.Unbox a => t -> a
at src/Main.hs:53:1
Expected type: U.MVector (PrimState (ST s)) a
Actual type: t
In the first argument of `MV.read', namely `mv'
In the second argument of `($)', namely `MV.read mv 0'
In the expression: runST $ MV.read mv 0
我可以用runST从可变向量中进行纯读取吗?如果是,如何?我认为它需要myRead
的类型签名,但我所尝试的一切只会导致越来越多无法理解的错误消息。
EDIT:突出显示我刚刚在下面的评论中添加的一些上下文:这里的上下文是我有一个函数,它接受一个可变向量,使用可变向量作为临时暂存空间进行一些计算,然后需要返回一个浮点值。因为我不关心可变向量的变化,我想知道是否有一种方法可以忽略它的"状态变化",只需返回其中一个值。
应用程序的答案告诉如何编译代码。但该代码将不可用:runST
的要点是,由于存在绑定类型变量,命令式计算无法逃脱它。
现在,您在某个地方创建的任何可变数组都将为固定的s具有类型MVector s a
,而您的myRead
需要一个为anys提供向量的值。
似乎之前有一个问题让你想要拥有那个(不可能的)功能。
编译器默认将参数mv
显示在左侧,作为某个特定类型,但需要在右侧显示多态类型。类型签名可以解决问题。
{-#LANGUAGE Rank2Types#-}
module Main where
import qualified Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST
myRead :: MV.Unbox a => (forall s . MV.MVector s a) -> a
myRead mv = runST $ MV.read mv 0
如果我理解的话,你的函数的潜在签名应该是这样的:
-- myBadRead :: forall s a . MV.Unbox a => MV.MVector s a -> a
-- myBadRead mv = runST $ MV.read mv 0
但是这里runST :: (forall s. ST s a) -> a
不会有一个独立于s
的东西来处理,因为当你在LHS上写mv
时,s
是固定的。
编辑:然而,正如Joachim B和Daniel F.所强调的,尽管上面的定义是连贯的,但它在实践中没有用处,因为你将无法构造一个向量mv
来给它。简单地说,任何生成mv
的方法都会在编译器的引擎盖下为它分配一个确定的s
。一种标准方法是使这样的函数作用于来自Data.Vector.Unboxed
的"纯"矢量,然后在右侧,在应用来自.Mutable
模块的操作之前将其解冻
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Unboxed.Mutable as MV
import Control.Monad.ST
myRead :: MV.Unbox a => UV.Vector a -> a
myRead v = runST $ do mv <- UV.unsafeThaw v
MV.read mv 0
当然,这个特定的定义等同于myRead = (UV.! 0)
。类似地,这样的东西是有意义的,在myRead
的定义中打击runST
mhead :: MV.Unbox a => MV.MVector s a -> ST s a
mhead mv0 = MV.read mv0 0
mrx = runST $ do mv <- UV.unsafeThaw $ UV.enumFromStepN 0 1 20
-- ^^^ or however the mv is generated.
x <- MV.unsafeRead mv 17 -- arbitrary 'scratch pad'
MV.unsafeWrite mv 17 (2*x) -- computations
mhead mv
-- ^^^ we return just the first element, after all the mutation
在这里,我们不是用runST
关闭myRead
或mhead
,而是在s
中保持它的多态性,然后可以在出现可变载体mv
的同一ST
块中使用它。因此,编译器将能够使用它用于do块的"秘密"s
作为一个整体来解释将mhead
应用于mv
的结果,因为这是我们对mhead
的多态定义留下的可能性之一
其他答案都很好,但我认为ST缺少一个基本的东西。对runST的每次调用都有效地创建了一个新的"ST宇宙",其中运行一些命令式代码。因此,如果您有一个对runST的调用来生成数组,而另一个对runST的调用则从该数组中获取值,那么事情就不可能成功。这两个runST调用想要它们自己独特的宇宙,而您希望它们共享一个宇宙。
答案详细解释了这些独特的宇宙是如何被某种类型的系统欺骗所创造的。