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

时间:2015-03-03 16:32:52

标签: haskell types

给出以下签名来运行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在评估环境中始终是免费的,避免泛化,我是对的吗?或者这里有更多关于通用量词的内容吗?

2 个答案:

答案 0 :(得分:6)

让我们读一下runST的类型。我也为a添加了明确的quatifier。

runST :: forall a . (forall s. ST s a) -> a

它读作以下合同:

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

    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被允许根据s选择a(请参阅上面的part3),因此必须修复a提前。

    现在,举个例子。问题在于

    runST (newSTRef ...)
    

    此处,newSTRef会返回ST s (STRef s Int),因此它会尝试选择a = STRef s Int。由于a取决于s,因此该选项无效。

    这个"技巧"由ST monad用于防止引用" escape"来自monad。也就是说,保证在runST返回之后,现在不再可以访问所有引用(并且可能它们可以被垃圾收集)。因此,ST计算期间使用的可变状态已被丢弃,runST的结果确实是值。毕竟,这是ST monad的主要目的:它意味着允许(临时)可变状态用于纯计算。

答案 1 :(得分:1)

我认为你错过了什么。 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’

Haskell编译器拒绝它,不是因为任何readVar有关,而是因为newVar存在问题,ST s (MutVar s a)允许s到&#34;逃避&#34;跳转到MutVar表达式的上下文。