s
中的STRef s a
是否使用具体类型进行实例化?可以很容易地想象一些代码STRef
在a
占用Int
的上下文中使用。但是类型推断似乎没有任何东西可以给s
一个具体的类型。
想象一下像MyList<S, A>
这样的伪Java。即使S
的实现中没有出现MyList
实例化MyList<S, Integer>
这样的具体类型,其中没有使用具体类型代替S
也没有意义。那么STRef s a
如何运作?
答案 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
。请注意runST
由runST 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
。