题
我有一个类型的绑定 [ST s (Int, [Int])]
我正在尝试申请 runST
使用映射到每个元素,如下所示:
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和函数组成, ,但我不确定这是否适用。
感谢大家的时间!
解决方案
每次运行状态转换器时 runST
, ,它在与所有其他状态转换器分离的某个本地状态上运行。 runST
创建一个 新的 状态类型并用该类型调用其参数。因此,例如,如果您执行
let x = runST (return ())
y = runST (return ())
in (x, y)
然后是第一个 return ()
第二个 return ()
将有不同的类型: ST s1 ()
和 ST s2 ()
, ,对于一些未知类型 s1
和 s2
是由创建的 runST
.
您正在尝试致电 runST
具有状态类型的参数 s
. 。这不是状态类型 runST
创建,也不是您可以选择的任何其他类型。打电话 runST
, ,你必须传递一个可以有的参数 任何 状态类型。这是一个例子:
r1 :: forall s. ST s ()
r1 = return ()
因为 r1
是多态的,它的状态可以有任何类型,包括选择的任何类型 runST
. 。你可以映射 runST
在多态列表上 r1
s(与 -XImpredicativeTypes
):
map runST ([r1, r1] :: [forall t. ST t ()])
但是,您无法映射 runST
在非多态列表上 r1
s。
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 ()]))
其他提示
你的 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 应该能够告诉您哪些扩展。
我将尝试解释原因 runST
的类型:
runST :: (forall s. ST s a) -> a
为什么它不像这么简单:
alternativeRunST :: ST s a -> a
请注意,这 alternativeRunST
会为你的程序工作。
alternativeRunST
也将允许我们从 ST
单子:
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
. 。类型系统不允许这样做。