7

当我尝试编译这个时:

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,但我尝试过的一切都导致了越来越多的难以理解的错误消息。

编辑:突出显示我刚刚在下面发表评论的一些上下文:这里的上下文是我有一个函数,它接收一个可变向量,使用可变向量作为临时暂存空间进行一些计算,然后需要返回一个浮点值。因为我不关心可变向量的变化,所以我想知道是否有一种方法可以忽略它的“状态变化”并简单地从其中返回一个值。

4

3 回答 3

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来赋予它。粗略地说,任何生成 an 的方式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出现的同一块内使用它。因此,编译器将能够使用s它用于整个 do-block 的“秘密”来解释 apply to 的结果mheadmv因为它是我们的多态定义留下的可能性之一mhead

于 2012-08-03T19:16:30.840 回答
3

applicative 的答案告诉你如何让你的代码编译。但是代码将无法使用:关键runST是命令式计算无法逃脱它,因为那里存在存在绑定的类型变量。

现在,您在某处创建的任何可变数组都将具有固定 s 的类型MVector s a您期望一个为任何smyRead提供向量的值。

之前似乎有一个问题让你想要拥有那个(不可能的)功能。

于 2012-08-03T19:22:29.613 回答
2

其他答案很好,但我认为您缺少关于 ST 的一件基本事情。对 runST 的每次调用都有效地创建了一个新的“ST 宇宙”,一些命令式代码在其中运行。因此,如果您调用 runST 以创建数组,并单独调用 runST 以从该数组中获取值,那么事情可能无法正常工作。两个 runST 调用想要它们自己独特的宇宙,而您希望它们共享一个。

详细解释的答案是这些独特的宇宙是如何通过一些类型系统的诡计创建的。

于 2012-08-06T01:55:47.357 回答