我有一个[ST s (Int, [Int])]
类型的绑定,我正在尝试使用map将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 and function composition,但我不确定这是否适用。
感谢每个人的时间!
答案 0 :(得分:15)
每次使用runST
运行状态转换器时,它都会在某个本地状态下运行,该状态与所有其他状态转换器分开。 runST
创建 new 状态类型,并使用该类型调用其参数。因此,例如,如果您执行
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
列表(-XImpredicativeTypes
):
map runST ([r1, r1] :: [forall t. ST t ()])
但是,您无法将runST
映射到非多态r1
列表中。
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 ()]))
答案 1 :(得分: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应该能够告诉你哪些扩展。
答案 2 :(得分:5)
我将尝试解释runST
类型的推理:
runST :: (forall s. ST s a) -> a
为什么它不像这么简单:
alternativeRunST :: ST s a -> a
请注意,此alternativeRunST
适用于您的计划。
alternativeRunST
也允许我们将变量泄露出ST
monad:
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
。类型系统不允许这样做。