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