当我尝试编译时:
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
的类型签名,但我尝试的所有内容都会导致越来越难以理解的错误消息。
编辑:突出显示一些上下文我刚才在下面的注释中:这里的上下文是我有一个函数,它接受一个可变向量,使用可变向量作为临时划痕进行一些计算空格,然后需要返回一个浮点值。因为我不关心可变矢量的变化,所以我想知道是否有办法忽略它的“状态变化”并简单地从其中返回一个值。
答案 0 :(得分:3)
编译器默认在某个特定类型的左侧看到参数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上编写s
时mv
已修复。
编辑:但是,正如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)
同样,类似的东西也有意义,在runST
myRead
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
此处,我们不是在myRead
关闭mhead
或runST
,而是在s
中保持多态,然后可以在同一ST
块中使用它其中出现了可变载体mv
。因此,编译器将能够使用它作为整体的do-block使用的'secret's
来解释将mhead
应用于mv
的结果,因为它是一个我们对mhead
答案 1 :(得分:3)
applicative的答案告诉你如何编译你的代码。但代码将无法使用:runST
的要点是命令式计算无法逃脱它,因为那里存在属性类型变量。
现在,您在某处创建的任何可变数组都会为修复提供类型MVector s a
,而您的myRead
需要一个为提供向量的值 s。
之前似乎有一个问题让你想要那个(不可能的)功能。
答案 2 :(得分:2)
其他答案都很好,但我认为ST缺少一个基本的东西。每次调用runST都会有效地创建一个新的“ST Universe”,其中运行一些命令式代码。因此,如果您有一次调用runST来生成数组并且单独调用runST来从该数组中获取值,那么事情就不可行了。两个runST调用需要它们自己的唯一Universe,而您希望它们共享一个。
答案中详细解释的是这些独特的宇宙是如何通过某种类型系统技巧创建的。