对一个人而不是另一个人有什么可能的知识?解决两者中任何一个限制的已知成语是什么?
在another question中,Andreas Rossberg指出在SML中适用于val rec
的限制:它必须是 fn-match 的形式,即使其他表达式也是如此有道理。
fun
语法没有这样的限制,但不能用于引入简单的绑定(我的意思是,只是一个带有可选类型注释的名称而不是其他内容),因为它需要参数暴露。
在一个较旧的问题中,我忽略了,有偏好的评论赞成或fun
超过val
/ val rec
。
我个人更多地使用val
/ val rec
,因为它揭示了自递归和非自递归绑定之间的区别(而自我递归暴露的可能实际上并不存在,另一个总是持有,暴露为非自递归的东西永远不会自我递归),也因为它使用与匿名lambda表达式相同的语法(更一致)。
这些是我所知道的。还有其他人吗?我不太了解任何解决方法的习语。他们是一些吗?
两者的限制在我看来只是语法上的,并没有真正的语义或健全背景。对于这些限制,这确实还是存在语义和健全背景?
如果它没有滥用,我会在一个片段下面发帖,这是上面链接的问题中发布的片段的变体。这个片段揭示了我遇到两个问题的情况(我对两者都不满意)。这些评论告诉我这两个问题在哪里,以及为什么这个问题在我看来。这个样本不能真正简化,因为这个问题是语法上的,所以真正的用例很重要。
(* ======================================================================== *)
(* A process chain. *)
datatype 'a process = Chain of ('a -> 'a process)
(* ------------------------------------------------------------------------ *)
(* An example controlling iterator using a process chain. it ends up to be
* a kind of co‑iteration (if that's not misusing the word). *)
val rec iter =
fn process: int -> int process =>
fn first: int =>
fn last: int =>
let
val rec step =
fn (i, Chain process) =>
if i < first then ()
else if i = last then (process i; ())
else if i > last then ()
else
let val Chain process = process i
in step (i + 1, Chain process)
end
in step (first, Chain process)
end
(* ------------------------------------------------------------------------ *)
(* A tiny test use case. *)
val rec process: int -> int process =
fn a: int =>
(print (Int.toString a);
Chain (fn a => (print "-";
Chain (fn a => (print (Int.toString a);
Chain (fn a => (print "|";
Chain process)))))))
(* Note the above is recursive: fn x => (a x; Chain (fn x => …)). We can't
* easily extract seperated `fn`, which would be nice to help composition.
* This is solved in the next section. *)
val () = iter process 0 20
val () = print "\n"
(* ======================================================================== *)
(* This section attempts to set‑up functions and operators to help write
* `process` in more pleasant way or with a more pleasant look (helps
* readability).
*)
(* ------------------------------------------------------------------------ *)
(* Make nested functions, parameters, with an helper function. *)
val chain: ('a -> unit) -> ('a -> 'a process) -> ('a -> 'a process) =
fn e =>
fn p =>
fn a => (e a; Chain p)
(* Now that we can extract the nested functions, we can rewrite: *)
val rec process: int -> int process =
fn a =>
let
val e1 = fn a => print (Int.toString a)
val e2 = fn a => print "-"
val e3 = fn a => print (Int.toString a)
val e4 = fn a => print "|"
in
(chain e1 (chain e2 (chain e3 (chain e4 process)))) a
end
(* Using this:
* val e1 = fn a => print (Int.toString a)
* val e2 = fn a => print "-"
* …
*
* Due to an SML syntactical restriction, we can't write this:
* val rec process = chain e1 (chain e2 ( … process))
*
* This requires to add a parameter on both side, but this, is OK:
* fun process a = (chain e1 (chain e2 ( … process))) a
*)
val e1 = fn a => print (Int.toString a)
val e2 = fn a => print "-"
val e3 = fn a => print (Int.toString a)
val e4 = fn a => print "|"
(* An unfortunate consequence of the need to use `fun`: the parameter added
* for `fun`, syntactically appears at the end of the expression, while it
* will be the parameter passed to `e1`. This syntactical distance acts
* against readability.
*)
fun process a = (chain e1 (chain e2 (chain e3 (chain e4 process)))) a
(* Or else, this, not better, with a useless `fn` wrapper: *)
val rec process = fn a =>
(chain e1 (chain e2 (chain e3 (chain e4 process)))) a
(* A purely syntactical function, to move the last argument to the front. *)
val start: 'a -> ('a -> 'b) -> 'b = fn a => fn f => f a
(* Now that we can write `start a f` instead of `f a`, we can write: *)
fun process a = start a (chain e1 (chain e2 (chain e3 (chain e4 process))))
infixr 0 THEN
val op THEN = fn (e, p) => (chain e p)
fun process a = start a (e1 THEN e2 THEN e3 THEN e4 THEN process)
(* This is already more pleasant (while still not perfect). Let's test it: *)
val () = iter process 0 20
val () = print "\n"
答案 0 :(得分:3)
val rec
表单计算最小的固定点。在一般情况下(至少不是严格的语言),这样的定点并不总是定义明确的或唯一的。特别是,如果右侧包含需要进行非平凡计算的表达式,那么递归绑定的含义应该是什么呢?这些计算已经取决于定义了什么?
没有有用的答案,因此SML(像许多其他语言一样)限制递归到(句法)函数。这样,它就像Y这样众所周知的固定点运算符有明确的语义解释,并且可以给出足够简单的评估规则。
当然,这同样适用于fun
。更具体地说,
fun f x y = e
仅被定义为
的语法糖val rec f = fn x => fn y => e
因此fun
必须至少有一个参数才能满足val rec
的句法要求。
答案 1 :(得分:1)
我会尝试回答我自己的问题。
对于由于语法限制而强制使用包装器fn
的情况(可能是考虑使用sML进行解决的问题?),我可以找到,而不是真正的解决方法,但是成语有助于减少这些案件的噪音。
我重复使用示例中的start
函数(请参阅问题),并将其重命名为n_equiv
,原因在评论中给出。这只需要一些先前的措辞来解释η等价是什么,并且还要说明合理定义和使用这个函数的语法限制(无论如何这总是有利于学习材料,我打算发布法国论坛上的一些SML样本。)
(* A purely syntactical function, to try to make forced use of `fn` wrappers
* a bit more acceptable. The function is named `n_equiv`, which refers to
* the η-equivalence axiom. It explicitly tells the construction has no
* effect. The function syntactically swap the function expression and its
* argument, so that both occurrences of the arguments appears close
* to each other in text, which helps avoid disturbance.
*)
val n_equiv: 'a -> ('a -> 'b) -> 'b = fn a => fn f => f a
问题中的示例中的用例,现在看起来像这样:
fun process a = n_equiv a (chain e1 (chain e2 (chain e3 (chain e4 process))))
…
fun process a = n_equiv a (e1 THEN e2 THEN e3 THEN e4 THEN process)
这已经更好了,因为现在人们清楚地告诉周围的结构是中立的。
要回答问题的其他部分,至少使用fun
而不是使用val rec
来处理此案例,与val rec
一样,n_equiv
自我记录成语无法应用。这是fun
超过val rec … = fn …
提及fun
与val
的比较详情的页面:TipsForWritingConciseSML (mlton.org)。请参阅页面中间的“Clausal函数定义”。对于非自递归函数,val … fn
比fun
更简洁,对于自递归函数可能会有所不同。