Haskell STRef和phantom类型
Haskell STRef和phantom类型,haskell,Haskell,STRef s a中的s是否使用具体类型实例化?我们可以很容易地想象一些代码,其中STRef在a呈现Int的上下文中使用。但是类型推断似乎没有任何东西可以给出一个具体的类型 想象一下伪Java中的一些东西,比如MyList。即使S从未出现在MyList的实现中,实例化像MyList这样的具体类型(其中未使用具体类型代替S)也没有意义。那么,STRef s a如何工作呢?tl;dr-在实践中,它似乎总是被初始化为真实世界最终 注意,s可以在stToIO的调用内部实例化为RealWorld,但在其他
STRef s a
中的s
是否使用具体类型实例化?我们可以很容易地想象一些代码,其中STRef
在a
呈现Int
的上下文中使用。但是类型推断似乎没有任何东西可以给出一个具体的类型
想象一下伪Java中的一些东西,比如
MyList
。即使S
从未出现在MyList
的实现中,实例化像MyList
这样的具体类型(其中未使用具体类型代替S
)也没有意义。那么,STRef s a如何工作呢?tl;dr-在实践中,它似乎总是被初始化为真实世界
最终
注意,s
可以在stToIO
的调用内部实例化为RealWorld
,但在其他方面未实例化:
--s参数为
--未实例化的类型变量(在“runST”调用内部),或
--“RealWorld”(内部调用“Control.Monad.ST.stToIO”)
查看ST
的实际代码,但是runST
似乎使用了一个特定的值realWorld
:
realWorld#
定义为:
您也可以在ghci中确认这一点:
Prelude> :set -XMagicHash
Prelude> :m +GHC.Prim
Prelude GHC.Prim> :t realWorld#
realWorld# :: State# RealWorld
从你的问题中,我看不出你是否理解为什么会有幻影
s
类型。即使你没有明确要求,我也要详细说明
幻影类型的角色
phantom类型的主要用途是将引用(也称为指针)限制在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,())
,具体取决于评估顺序。在纯粹的哈斯克尔世界里,这是一个很大的禁忌
这里的问题是,var
是从其runST
中“转义”的引用。当您退出ST monad时,您不再被迫使用monad运算符>=
(或者等效地,使用do
符号来按顺序排列副作用顺序。如果引用仍然存在,那么我们仍然可以在没有副作用的情况下产生副作用
为了避免这个问题,我们将runST
限制在stsa
上,其中a
不依赖于s
。为什么会这样?因为newSTRef
返回一个STRef sa
,对a
的引用被标记为幻影类型s
,因此返回类型取决于s无法通过runST
从ST单子中提取
从技术上讲,此限制通过使用秩-2类型来实现:
runST :: (forall s. ST s a) -> a
“福尔”这里是用来实现限制的。类型是这样的:选择你想要的任何a
,然后为我想要的任何s
提供类型ST s a
的值,然后我将返回一个a
。注意s
是由runST
选择的,而不是由调用者选择的,因此它可以是绝对任何类型。因此,类型syste只有在s
不受约束且a
不涉及s
的情况下,m才会接受应用程序runST操作
(请记住,调用方必须在runST
选择s
之前选择a
)
实施独立性约束确实是一个略显粗俗的把戏,但它确实奏效
关于实际问题
将这一点与您的实际问题联系起来:在runST
的实现中,s
将被选择为任何具体类型。请注意,即使s
被简单地选择为Int
在runST
内部,这也没有多大关系,因为类型系统已经将a
约束为独立的来自s
的ent,因此无需参考。正如@Ganesh指出的,RealWorld
是GHC使用的类型
您还提到了Java,可以尝试在Java中玩类似的把戏,如下所示:(警告,下面是过于简化的代码)
interface ST{A call();}
接口中断{ST调用(S虚拟);}
...
runST(STAction action}{
真实世界虚拟=新真实世界();
返回action.call(dummy.call();
}
上面的STAction
参数A
不能依赖于S@CarstenKönig我不确定它是否在某个阶段被编译出来,但在GHC核心多态类型变量被显式地作为函数参数应用,类似于pi类型在依赖类型语言中的工作方式。@davidyong谢谢!正如我所说:I我不知道它是如何在后台处理的-有什么资源可以让我自学吗?我也看到了-我必须承认,我不知道#
都是关于什么的-你能在这里帮我吗?我相信通常只指示未绑定的东西(例如元组),尽管State 35;
是GHC内部实现的一部分,强制对IO
monad进行正确排序,因此可能更神奇一些。
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
runST :: (forall s. ST s a) -> a
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();
}