Haskell 通用量化和统一,一个例子

Haskell 通用量化和统一,一个例子,haskell,types,Haskell,Types,为运行monadST runST :: (forall s. ST s a) -> a 以及功能 newVar :: a -> ST s (MutVar s a) readVar :: MutVar s a -> ST s a 然后Haskell编译器将拒绝以下类型错误的表达式 let v = runST (newVar True) in runST (readVar v) 因为为了计算runST,需要 readVar v :: ST s Bool 必须推广到 ∀

为运行monad
ST

runST :: (forall s. ST s a) -> a
以及功能

newVar  :: a -> ST s (MutVar s a) 
readVar :: MutVar s a -> ST s a
然后Haskell编译器将拒绝以下类型错误的表达式

let v = runST (newVar True)
in runST (readVar v)
因为为了计算
runST
,需要

readVar v :: ST s Bool 
必须推广到

∀s . ST s Bool 

我的问题是,通用量词在这里的唯一工作是确保类型变量
s
在求值上下文中始终是自由的,避免泛化,对吗?或者这里还有关于通用量词的更多信息吗?

我想你遗漏了一些东西。GHCi给出的实际信息是:

Prelude> :m +Control.Monad.ST
Prelude Control.Monad.ST> data MutVar s a = MutVar
Prelude Control.Monad.ST> :set -XRankNTypes
Prelude Control.Monad.ST> data MutVar s a = MutVar
Prelude Control.Monad.ST> let readVar = undefined :: MutVar s a -> ST s a
Prelude Control.Monad.ST> let newVar = undefined :: a -> ST s (MutVar s a)
Prelude Control.Monad.ST> runST $ readVar $ runST $ newVar True

<interactive>:14:27:
    Couldn't match type ‘s’ with ‘s1’
    ‘s’ is a rigid type variable bound by
        a type expected by the context: ST s Bool at <interactive>:14:1
    ‘s1’ is a rigid type variable bound by
        a type expected by the context: ST s1 (MutVar s Bool)
        at <interactive>:14:19
    Expected type: ST s1 (MutVar s Bool)
    Actual type: ST s1 (MutVar s1 Bool)
    In the second argument of ‘($)’, namely ‘newVar True’
    In the second argument of ‘($)’, namely ‘runST $ newVar True’
前奏曲>:m+Control.Monad.ST
前奏曲控件.Monad.ST>数据MutVar s a=MutVar
前奏曲控制.Monad.ST>:set-XRankNTypes
前奏曲控件.Monad.ST>数据MutVar s a=MutVar
Prelude Control.Monad.ST>let readVar=undefined::MutVar s a->ST s a
Prelude Control.Monad.ST>let newVar=undefined::a->ST s(MutVar s a)
Prelude Control.Monad.ST>runST$readVar$runST$newVar True
:14:27:
无法将类型“s”与“s1”匹配
“s”是一个刚性类型变量,由
上下文所需的类型:ST s Bool at:14:1
“s1”是一个刚性类型变量,由
上下文所需的类型:ST s1(MutVar s Bool)
时间:14:19
预期类型:ST s1(多变量s布尔)
实际类型:ST s1(多变量s1布尔)
在“($)”的第二个参数中,即“newVar True”
在“($)”的第二个参数中,即“runST$newVar True”

Haskell编译器拒绝它不是因为与
readVar
有任何关系,而是因为
newVar
有一个问题,即
sts(MutVar sa)
允许
s
通过跳入
MutVar
表达式来“逃逸”其上下文。

让我们看看
runST
的类型。我还为
a
添加了一个显式的四元组

runST :: forall a . (forall s. ST s a) -> a
合同内容如下:

  • 调用者选择固定类型
    a
  • 调用者提供一个参数
    x
  • 对于
    s
    的任何选择,参数
    x
    必须是
    ST s a
    类型。换句话说,
    s
    将由
    runST
    选择,而不是由调用者选择
  • 让我们看一个类似的例子:

    runFoo :: forall a . (forall s. s -> [(s,a)]) -> [a]
    runFoo x =
        let part1 = x "hello!"          -- here s is String
            -- part1 has type [(String, a)]
            part2 = x 'a'               -- here s is Char
            -- part2 has type [(Char, a)]
            part3 = x (map snd part2)   -- here s is [a]   (!!!)
            -- part3 has type [([a],a)]
        in map snd part1 ++ map snd part2 ++ map snd part3
    
    test1 :: [Int]
    test1 = runFoo (\y -> [(y,2),(y,5)])   -- here a is Int
    
    test2 :: [Int]
    test2 = runFoo (\y -> [("abc" ++ y,2)])       -- ** error
    -- I can't choose y :: String, runFoo will choose that type!
    
    请注意,
    a
    是固定的(到
    Int
    ),并且我不能对
    y
    的类型设置任何限制。此外:

    test3 = runFoo (\y -> [(y,y)])    -- ** error
    
    在这里,我不是预先修复
    a
    ,而是尝试选择
    a=s
    。我不允许这样做:
    runFoo
    可以根据
    a
    选择
    s
    (请参见上文
    part3
    ),因此
    a
    必须提前修复

    现在,以你为例。问题在于

    runST (newSTRef ...)
    
    在这里,
    newSTRef
    返回一个
    ST s(STRef s Int)
    ,因此它试图选择
    a=STRef s Int
    。由于
    a
    依赖于
    s
    ,因此此选项无效


    ST
    monad使用此“技巧”来防止引用monad中的“转义”。也就是说,可以保证在
    runST
    返回后,所有引用现在都不再可访问(并且可能会被垃圾收集)。因此,
    ST
    计算过程中使用的可变状态被丢弃,而
    runST
    的结果实际上是一个纯值。毕竟,这是
    ST
    monad的主要目的:它意味着允许在纯计算中使用(临时)可变状态。

    您所说的“在评估上下文中始终自由,避免泛化”是什么意思?我想你可能对此有正确的想法,但我无法解析这个句子。如果我错了,请更正。在上下文Γ中推广t型,需要量化t的自由变量,这些变量不是在Γ上自由出现的。我的意思是,因为s在t和Γ中都是自由的,所以这个类型不能被推广,然后被拒绝。