Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell STRef和phantom类型_Haskell - Fatal编程技术网

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
,因此返回类型取决于
srunST
从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();
}