英文:
Difference between let+, let* and let()?
问题
由于OCaml的文档有限,我会很感激如果有人能够解释不同用法的let之间的区别。
我尝试查看https://dev.realworldocaml.org/toc.html,但网站没有简便的搜索方式。谷歌搜索引导我到一些文章,但没有得到确切的解释。
英文:
As the documentation on OCaml is sparse, i would appreciate if some one can explain the difference in different flavors of let usage.
I tried looking into https://dev.realworldocaml.org/toc.html, but there is no easy way to search in the website. Google search landed me to some articles, but did not get the exact explanation.
答案1
得分: 5
let
表达式的基本形式是:
let p1 = e1
and p2 = e2
...
and pN = eN
in e
其中N
至少为1。在这种形式下,let
表达式模式匹配通过对LHS模式对RHS表达式进行评估而产生的值,然后使用LHS模式定义的新绑定在范围内评估主体。例如,
let x, y = 1, 2 in
x + y
评估结果为3。
当let
附有操作符名称时,它是所谓的“let操作符”或“绑定操作符”的应用(以便更容易搜索)。例如:
let+ x, y = 1, 2 in
x + y
解糖为(let+) (1, 2) (fun (x, y) -> x + y)
。(类似于将操作符+
用括号括起来,使其成为(+)
,以引用其标识符,let操作符let+
的标识符,就像它出现在let表达式中一样,将是(let+)
。)
最后,当let
绑定附有操作符名称时,所有的and
绑定也必须附有操作符名称。
let* x = 1
and+ y = 2
and* z = 3 in
x + y + z
解糖为(let*) ((and+) 1 ((and*) 2 3)) (fun ((x, y), z) ->)
。
以下程序是无效的,并且没有意义,因为let
绑定被用作操作符,但and
绑定没有:
let* x = 1
and y = 2 in
x + y
这里介绍了绑定操作符,位于OCaml文档的“语言扩展”部分。
let () = e
只是模式匹配的非操作符形式,其中()
是匹配unit
类型的唯一值的模式。unit
类型通常是不会评估为有意义值的表达式的类型,但存在于副作用(例如print_endline "Hello world!"
)的情况下。匹配()
可以确保表达式具有类型()
,从而捕获部分应用错误。以下是类型检查的:
let f x y =
print_endline x;
print_endline y
let () =
f "Hello" "World"
以下则不会:
let f x y =
print_endline x;
print_endline y
let () =
f "Hello" (*缺少第二个参数,因此表达式的类型为string -> unit,而不是unit*)
请注意,绑定操作符对于方便使用“monad”和“applicative”非常有用,因此在学习绑定操作符时可能会听到这些词。但是,绑定操作符与这些概念本质上没有直接关联。它们只是解糖为我上面描述的表达式,任何其他意义(例如与monad的关系)都取决于操作符的定义方式。
英文:
The basic form of let expressions is:
let p1 = e1
and p2 = e2
...
and pN = eN
in e
where N
is at least 1. In this form, let expressions pattern matches the value that results from evaluating the RHS expressions against the LHS patterns, then evaluates the body with the new bindings defined by the LHS patterns in scope. For example,
let x, y = 1, 2 in
x + y
evaluates to 3.
When let
has an operator name attached, it is the application of what is called a "let operator" or "binding operator" (to give you easier terms to search up). For example:
let+ x, y = 1, 2 in
x + y
desugars to (let+) (1, 2) (fun (x, y) -> x + y)
. (Similar to how one surrounds the operator +
in parentheses, making it (+)
, to refer to its identifier, the identifier for the let operator let+
, as it appears in a let expression, would be (let+)
.)
Finally, when a let
binding has an operator name attached, all the and
bindings must have operator names attached as well.
let* x = 1
and+ y = 2
and* z = 3 in
x + y + z
desugars to (let*) ((and+) 1 ((and*) 2 3)) (fun ((x, y), z) ->)
.
The following program is invalid and has no meaning because the let
binding is being used as an operator, but the and
binding is not:
let* x = 1
and y = 2 in
x + y
Binding operators are covered in the "language extensions" section of the OCaml documentation.
let () = e
is merely the non-operator form of a pattern match, where ()
is the pattern that matches the only value of the unit
type. The unit
type is conventionally the type of expressions that don't evaluate to a meaningful value, but exist for side effects (e.g. print_endline "Hello world!"
). Matching against ()
ensures that the expression has type ()
, catching partial application errors. The following typechecks:
let f x y =
print_endline x;
print_endline y
let () =
f "Hello" "World"
The following does not:
let f x y =
print_endline x;
print_endline y
let () =
f "Hello" (* Missing second argument, so expression has type string -> unit, not unit *)
Note that the binding operators are useful for conveniently using "monads" and "applicatives," so you may hear these words when learning about binding operators. However, binding operators are not inherently related to these concepts. All they do is desugar to the expressions that I describe above, and any other significance (such as relation to monads) results from how the operator was defined.
答案2
得分: 4
考虑以下来自 OCaml 页面上关于 let operators 的代码:
let ( let* ) o f =
match o with
| None -> None
| Some x -> f x
let return x = Some x
如果我们创建一个非常简单的映射:
module M = Map.Make (Int)
let m = M.(empty |> add 1 4 |> add 2 3 |> add 3 7)
如果我们想要编写一个函数,该函数接受一个映射和两个键,并添加这些键处的值,返回 int option
,我们可以这样编写:
let add_values m k1 k2 =
match M.find_opt k1 m with
| None -> None
| Some v1 ->
match M.find_opt k2 m with
| None -> None
| Some v2 ->
Some (v1 + v2)
当然,有多种定义这个函数的方式。我们可以这样做:
let add_values m k1 k2 =
match (M.find_opt k1 m, M.find_opt k2 m) with
| (None, _) | (_, None) -> None
| (Some v1, Some v2) -> Some (v1 + v2)
或者利用异常的机会:
let add_values m k1 k2 =
try
Some (M.find k1 m + M.find k2 m)
with
| Not_found -> None
Let operators 允许我们这样写:
let add_values m k1 k2 =
let* v1 = M.find_opt k1 m in
let* v2 = M.find_opt k2 m in
return (v1 + v2)
英文:
Consider the following code from the OCaml page on let operators.
>
> let ( let* ) o f =
> match o with
> | None -> None
> | Some x -> f x
>
> let return x = Some x
>
If we create a very simply map:
module M = Map.Make (Int)
let m = M.(empty |> add 1 4 |> add 2 3 |> add 3 7)
If we wanted to write a function that takes a map and two keys and adds the values at those keys, returning int option
, we might write:
let add_values m k1 k2 =
match M.find_opt k1 m with
| None -> None
| Some v1 ->
match M.find_opt k2 m with
| None -> None
| Some v2 ->
Some (v1 + v2)
Now, of course there are multiple ways of defining this. We could:
let add_values m k1 k2 =
match (M.find_opt k1 m, M.find_opt k2 m) with
| (None, _) | (_, None) -> None
| (Some v1, Some v2) -> Some (v1 + v2)
Or take advantage of exceptions:
let add_values m k1 k2 =
try
Some (M.find k1 m + M.find k2 m)
with
| Not_found -> None
Let operators let us write:
let add_values m k1 k2 =
let* v1 = M.find_opt k1 m in
let* v2 = M.find_opt k2 m in
return (v1 + v2)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论