Haskell 哈斯克尔:地图朗斯特

Haskell 哈斯克尔:地图朗斯特,haskell,state-monad,Haskell,State Monad,我有一个类型[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

我有一个类型
[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
运行状态转换器时,它都会在与所有其他状态转换器分离的某个本地状态上运行
runST
创建一个新的状态类型,并使用该类型调用其参数。例如,如果执行

let x = runST (return ())
    y = runST (return ())
in (x, y)
然后,第一个
return()
和第二个
return()
将具有不同的类型:
ST s1()
ST s2()
,用于
runST
创建的一些未知类型
s1
s2

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

r1 :: forall s. ST s ()
r1 = return ()
因为
r1
是多态的,所以它的状态可以有任何类型,包括
runST
选择的任何类型。您可以在多态的
r1
s列表上映射
runST
(使用
-ximpeditivetypes
):

但是,不能在非多态的
r1
s列表上映射
runST

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

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

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

您的
名称
多态性不够。你的陈述

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类型在列表的每个元素中可能不同,它可能与
名称
中的类型不同。更改
名称的类型签名
,一切都应该正常。它可能需要启用一些扩展,但GHC应该能够告诉您哪些扩展。

我将尝试解释
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)
函数
的引用不透明

顺便说一句,要自己尝试一下,下面是运行上述代码所需的“坏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”
不适合
(适用于所有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
这相当于像我们以前做的那样,对所有a.都省略了


请注意,
a
的范围大于
s
,但在
newSTRef“Hi”
的情况下,其值应取决于
s
。类型系统不允许这种情况。

@yairchu能否请您详细说明为什么它在使用
dontEvenRunST
时失败,这里对此进行了解释:我看到的每一篇文章都提到它与参数和返回类型之间的状态类型不匹配有关,但您可以说明,即使返回类型是其他类型(如
Int
)它仍然不会进行打字检查。
>>> 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])
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