STRef和幻像类型

时间:2014-10-08 04:39:04

标签: haskell

s中的STRef s a是否使用具体类型进行实例化?可以很容易地想象一些代码STRefa占用Int的上下文中使用。但是类型推断似乎没有任何东西可以给s一个具体的类型。

想象一下像MyList<S, A>这样的伪Java。即使S的实现中没有出现MyList实例化MyList<S, Integer>这样的具体类型,其中没有使用具体类型代替S也没有意义。那么STRef s a如何运作?

2 个答案:

答案 0 :(得分:4)

tl; dr - 在实践中似乎总是初始化为RealWorld

source注意s可以在RealWorld的调用中实例化为stToIO,但是未实例化:

  

- s参数是

     

- 未实例化的类型变量(在'runST'内部调用)或

     

- 'RealWorld'(内部调用'Control.Monad.ST.stToIO')。

查看ST的实际代码但似乎runST使用特定值realWorld#

newtype ST s a = ST (STRep s a)
type STRep s a = State# s -> (# State# s, a #)

runST :: (forall s. ST s a) -> a
runST st = runSTRep (case st of { ST st_rep -> st_rep })

runSTRep :: (forall s. STRep s a) -> a
runSTRep st_rep = case st_rep realWorld# of
                        (# _, r #) -> r

realWorld#定义为magic primitive inside the GHC source code

realWorldName     = mkWiredInIdName gHC_PRIM  (fsLit "realWorld#")
                                    realWorldPrimIdKey realWorldPrimId

realWorldPrimId :: Id   -- :: State# RealWorld
realWorldPrimId = pcMiscPrelId realWorldName realWorldStatePrimTy
                     (noCafIdInfo `setUnfoldingInfo` evaldUnfolding
                                  `setOneShotInfo` stateHackOneShot)

您也可以在ghci

中进行确认
Prelude> :set -XMagicHash
Prelude> :m +GHC.Prim
Prelude GHC.Prim> :t realWorld#
realWorld# :: State# RealWorld

答案 1 :(得分:4)

从你的问题我看不出你是否理解为什么幻影s类型完全存在。即使您没有明确要求,请让我详细说明。

幻像类型的作用

幻像类型的主要用途是约束引用(又名指针)以保持在ST monad“内部”。粗略地说,动态分配的数据必须在runST返回时终止。

要查看问题,我们假设runST的类型为

runST :: ST s a -> a

然后,考虑一下:

data Dummy
let var :: STRef Dummy Int
    var    = runST (newSTRef 0)
    change :: () -> ()
    change = runST (modifySTRef var succ)
    access :: () -> Int
    result :: (Int, ())
    result = (access() , change())
 in result

(上面我添加了一些无用的()参数,使其类似于命令式代码)

现在上面代码的结果应该是什么?它可以是(0,())(1,()),具体取决于评估顺序。在纯粹的Haskell世界中,这是一个很大的禁忌。

此处的问题是var是从runST“转义”的引用。当您逃离ST monad时,您不再被迫使用monad运算符>>=(或等效地,do表示法来排序副作用的顺序。如果引用仍然存在,那么我们可以什么时候应该没有副作用。

为避免此问题,我们将runST限制为ST s a ,其中a不依赖s 。为什么这个?由于newSTRef会返回STRef s a,因此引用a标记为幻像类型s,因此返回类型取决于s,并且无法从runST中提取ST monad通过runST :: (forall s. ST s a) -> a

从技术上讲,这种限制是通过使用rank-2类型完成的:

a

这里的“forall”用于实现限制。类型是:选择您想要的任何ST s a,然后为我希望的任何s提供a类型的值,然后我将返回s。请注意runSTrunST action选择,而不是由来电者选择,所以它绝对是任何东西。因此,只有当action :: forall s. ST s a s不受约束且a不涉及s时,类型系统才会接受应用a(回想一下来电者有在runST选择s之前选择runST

实现独立约束确实是一种稍微狡猾的技巧,但确实有效。

关于实际问题

要将其与您的实际问题联系起来:在s的实施中,s将被选为任何具体类型。请注意,即使Int被简单地选择为runST a,也无关紧要,因为类型系统已将约s限制为独立于{{} 1}},因此无参考。正如@Ganesh指出的那样,RealWorld是GHC使用的类型。

您还提到了Java。有人可能会尝试在Java中使用类似的技巧:(警告,过度简化的代码)

interface ST<S,A> { A call(); }
interface STAction<A> { <S> ST<S,A> call(S dummy); }
...
<A> A runST(STAction<A> action} {
    RealWorld dummy = new RealWorld();
    return action.call(dummy).call();
}

STAction参数A上方不能依赖S