14

我有一个类型的绑定[ST s (Int, [Int])],我正在尝试runST使用 map 应用到每个元素,如下所示:

name :: [ST s (Int, [Int])] --Of Course there is a real value here
map runST name

这给了我一条错误消息

Couldn't match expected type `forall s. ST s b0'
    with actual type `ST s0 (Int, [Int])'
Expected type: [forall s. ST s b0]
  Actual type: [ST s0 (Int, [Int])]
In the second argument of `map', namely `name'
In the expression: map runST name

我一定有什么误解。我知道runST 和 function composition,但不确定这是否适用。

感谢大家的时间!

4

3 回答 3

15

每次使用 运行状态转换器时runST,它都会在与所有其他状态转换器分开的某个本地状态上运行。 runST创建一个的状态类型并使用该类型调用其参数。因此,例如,如果您执行

let x = runST (return ())
    y = runST (return ())
in (x, y)

那么第一个return ()和第二个return ()将有不同的类型:ST s1 ()ST s2 (),对于一些未知类型s1s2runST.

您正在尝试runST使用具有 state 类型的参数进行调用s。这不是runST创建的状态类型,也不是您可以选择的任何其他类型。要调用runST,您必须传递一个可以具有任何状态类型的参数。这是一个例子:

r1 :: forall s. ST s ()
r1 = return ()

因为r1是多态的,所以它的状态可以有任何类型,包括被 . 选择的任何类型runST。您可以映射runST多态r1s 的列表(使用-XImpredicativeTypes):

map runST ([r1, r1] :: [forall t. ST t ()])

但是,您不能映射runST非多态r1s 的列表。

map runST ([r1, r1] :: forall t. [ST t ()]) -- Not polymorphic enough

该类型forall t. [ST t ()]表示所有列表元素都有状态类型t。但是它们都需要具有独立的状态类型,因为runST每个状态类型都被调用。这就是错误消息的含义。

如果可以为所有列表元素赋予相同的状态,那么您可以调用runST一次,如下所示。不需要显式类型签名。

runST (sequence ([r1, r1] :: forall t. [ST t ()]))
于 2012-07-26T05:51:43.520 回答
6

name的多态性不够。你的陈述

name :: [ST s (Int, [Int])]

表示“返回 (Int, [Int]) 的有状态计算列表,它们具有完全相同的 s”。但看看类型runST

runST :: (forall s. ST s a) -> a

这种类型的意思是“一个进行有状态计算的函数,它s 可以是你能想象到的任何东西”。这些类型的计算不是一回事。最后:

map runST :: [forall s. ST s a] -> [a]

你看,你的列表应该比现在包含更多的多态值。s列表的每个元素中的类型可能不同,它可能与中的类型不同name。更改 的类型签名name,一切都应该没问题。它可能需要启用一些扩展,但 GHC 应该能够告诉您哪些扩展。

于 2012-07-26T05:51:57.810 回答
5

我将尝试解释runST' 类型的原因:

runST :: (forall s. ST s a) -> a

为什么它不像这个简单的:

alternativeRunST :: ST s a -> a

请注意,这alternativeRunST对您的程序有效。

alternativeRunST还允许我们从STmonad 中泄漏变量:

leakyVar :: STRef s Int
leakyVar = alternativeRunST (newSTRef 0)

evilFunction :: Int -> Int
evilFunction x =
  alternativeRunST $ do
    val <- readSTRef leakyVar
    writeSTRef leakyVar (val+1)
    return (val + x)

然后你可以进入 ghci 并执行以下操作:

>>> map evilFunction [7,7,7]
[7,8,9]

evilFunction不是参照透明的!

顺便说一句,要自己尝试一下,这是运行上述代码所需的“坏 ST”框架:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad
import Data.IORef
import System.IO.Unsafe
newtype ST s a = ST { unST :: IO a } deriving Monad
newtype STRef s a = STRef { unSTRef :: IORef a }
alternativeRunST :: ST s a -> a
alternativeRunST = unsafePerformIO . unST
newSTRef :: a -> ST s (STRef s a)
newSTRef = ST . liftM STRef . newIORef
readSTRef :: STRef s a -> ST s a
readSTRef = ST . readIORef . unSTRef
writeSTRef :: STRef s a -> a -> ST s ()
writeSTRef ref = ST . writeIORef (unSTRef ref)

真实runST不允许我们构造这样的“邪恶”功能。它是如何做到的?这有点棘手,见下文:

试图运行:

>>> runST (newSTRef "Hi")
error:
    Couldn't match type `a' with `STRef s [Char]'
...

>>> :t runST
runST :: (forall s. ST s a) -> a
>>> :t newSTRef "Hi"
newSTRef "Hi" :: ST s (STRef s [Char])

newSTRef "Hi"不适合(forall s. ST s a)。使用一个更简单的例子也可以看出,GHC 给了我们一个非常好的错误:

dontEvenRunST :: (forall s. ST s a) -> Int
dontEvenRunST = const 0

>>> dontEvenRunST (newSTRef "Hi")
<interactive>:14:1:
    Couldn't match type `a0' with `STRef s [Char]'
      because type variable `s' would escape its scope

注意我们也可以写

dontEvenRunST :: forall a. (forall s. ST s a) -> Int

它相当于forall a.像我们之前所做的那样省略了。

注意 的范围a比的要大s,但在情况下newSTRef "Hi"它的值应该取决于s。类型系统不允许这样做。

于 2012-07-26T23:01:54.073 回答