Logic 汞的ADT性质
我不明白为什么Mercury(10.04)不能推断出下一个片段的决定论:Logic 汞的ADT性质,logic,declarative,mercury,Logic,Declarative,Mercury,我不明白为什么Mercury(10.04)不能推断出下一个片段的决定论: :- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det. load_freqs(CPU, ResFreqs, !IO):- open_input(cpu_fn(CPU, "available_frequencies"), ResStream, !IO), (ResStream = io.ok(Stream) -
:- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det.
load_freqs(CPU, ResFreqs, !IO):-
open_input(cpu_fn(CPU, "available_frequencies"), ResStream, !IO),
(ResStream = io.ok(Stream) ->
ResFreqs = io.ok([])
;ResStream = io.error(Err),
ResFreqs = io.error(Err)
).
它抱怨说:
cpugear.m:075: In `load_freqs'(in, out, di, uo):
cpugear.m:075: error: determinism declaration not satisfied.
cpugear.m:075: Declared `det', inferred `semidet'.
cpugear.m:080: Unification of `ResStream' and `io.error(Err)' can fail.
cpugear.m:076: In clause for predicate `cpugear.load_freqs'/4:
cpugear.m:076: warning: variable `CPU' occurs only once in this scope.
cpugear.m:078: In clause for predicate `cpugear.load_freqs'/4:
cpugear.m:078: warning: variable `Stream' occurs only once in this scope.
更新#1:
即使在以下情况下,它也可以决定det:
:- pred read_freqs(bool::in, io.res(io.input_stream)::in, io.res(list(float))::out, io::di, io::uo) is det.
read_freqs(no, ResStream, io.ok([]), IO, IO):- ResStream = io.ok(_).
read_freqs(F, io.ok(_), io.ok([]), IO, IO):- F = yes.
read_freqs(yes, io.error(Err), io.error(Err), IO, IO).
read_freqs(F, ResStream, io.error(Err), IO, IO):- ResStream = io.error(Err), F = no.
我对Mercury的条件决定论规则(见下文)的理解是,要将其视为确定性,您应该将
->
替换为,
汞参考手册:
If-then-else的条件
不能失败,if-then-else是
相当于
条件和“然后”部分,及其
决定论据此计算。
否则,如果
“then”部分或“else”部分
部分可能会失败
好的,它可以推断出:
:- pred load_freqs(int::in, io.res(list(float))::out, io::di, io::uo) is det.
load_freqs(CPU, ResFreqs, !IO):-
open_input(cpu_fn(0, "available_frequencies"), ResStream, !IO),
(ResStream = io.ok(Stream),
ResFreqs = io.ok([])
;ResStream = io.error(Err),
ResFreqs = io.error(Err)
).
但是为什么“if-then-else”结构引入了semidet?至于“为什么”。让我们用if-then-else查看原始代码:
(ResStream = io.ok(Stream) ->
ResFreqs = io.ok([])
;ResStream = io.error(Err),
ResFreqs = io.error(Err)
).
如果条件失败,则else情况下的第一个连接是semidet测试。编译器不知道它必须成功(可以通过知道此条件失败来推断)。换句话说,编译器不够聪明
也就是说,很少会发现这个问题,因为通常情况下,条件更复杂,不允许进行这种推断,因此编译器不够聪明,无法确定正确的决定论并不重要
建议尽可能使用开关编程(如本例),这样可以防止当前问题,并有助于确保您已涵盖所有可能的ResStream案例。例如,如果将来io.error被重新分解,可能是io.error\u文件未找到或io.error\u磁盘已满等,编译器将指示程序员修复其开关,因为它现在不完整。在这种情况下,
ResStream
可以与io.ok(Stream)
统一,但也可以与io.error(Err)
统一。这取决于打开文件的结果。这有两个分支。获得独家“或”我使用“如果其他”结构,并根据您的引用,它应该适合。但是Mercury不能推断不是(ResStream=ok()))=>ResStream=error()
。在你的例子中,else分支是ResStream=io.error(Err),ResFreqs=io.error(Err)
,如果ResStream是io.ok(X),它将失败。我引用的规则没有说明任何关于添加条件的否定。我同意逻辑上它可以被添加,但参考手册表明它不是。相反,如果析取有一个“开关”的形式,则会被特别处理——它似乎允许析取中的连词,但不允许条件句。我猜原因是,在普通情况下,模式不相交,在这种情况下,“和“->”做同样的事情。模式可以相交,Mercury发现这些相交与推断决定论“多重”的相交。请参阅其他示例。如果您仅通过head决定det.,则read\u freqs
(更新中)将被标记为multi。我对“为什么?”的第一个回答是因为Mercury参考手册中说应该这样做。但是,我猜你的意思是“为什么水星是这样设计的?”。我的猜测是,开关需要以一种特殊的方式进行处理,设计师不想让它们变得比必要的更复杂,避免负面信息确实简化了事情。另外,我的印象是,Mercury通常鼓励优先于条件句的转换。事实上,有一个开关--INFORT ite而不是switch可以报告应该被开关替换的条件。谢谢,这是一个有用的选项--“INFORT ite而不是switch”。现在,对我来说,通过(X=yes,true;not(X=yes),X=no)
(这是我对的期望)(X=yes->true;X=no)
)将被推断为未设置,因为不是/1
。这是真的,直到条件变得更复杂。如果符号多于yes或no,或者条件中有多个统一。
(ResStream = io.ok(Stream) ->
ResFreqs = io.ok([])
;ResStream = io.error(Err),
ResFreqs = io.error(Err)
).