理解let和val在交互模式下的关系


let app = fun f -> fun x -> f (x);;
(*val app : ('a -> 'b) -> 'a -> 'b = <fun>*)
let app = fun f -> fun x -> x (f+1);;
(*val app : int -> (int -> 'a) -> 'a = <fun>*)
let app = fun f -> fun x -> x (f);;
(*val app : 'a -> ('a -> 'b) -> 'b = <fun>*)
let app2 = fun f -> fun g -> fun x -> g ( f x );;
(*val app2 : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun>*)
let app2 = fun f -> fun g -> fun x -> f (g x );;
(*val app2 : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>*)
let app2 = fun f -> fun g -> fun x -> g (f x+1);;
(*val app2 : ('a -> int) -> (int -> 'b) -> 'a -> 'b = <fun>*)
let app2 = fun f -> fun g -> fun x -> f (g x+1 );;
(*val app2 : (int -> 'a) -> ('b -> int) -> 'b -> 'a = <fun>*)
let app3 = fun f -> fun g -> fun x -> g f x+1 ;;
(*val app3 : 'a -> ('a -> 'b -> int) -> 'b -> int = <fun>*)

我如何知道val行发生了什么,而不按let app行上的enter键。更具体地说,我不明白letval之间的关系。

例如,第一个:

let app = fun f -> fun x -> f (x);;
(*val app : ('a -> 'b) -> 'a -> 'b = <fun>*)

我看到的是第一个x的名称为'a然后进入f,命名为'b然后转到有趣的x,也就是'a然后转到有趣的y也就是'b

但是对于其他函数显然不是这样。我怎么知道它们之间的关系呢?

有人把这些函数的类型放在实际代码下面的注释中。OCaml中的注释被(**)包围。

这些类型签名与您在OCaml顶层中看到的相同。您输入一个表达式,后面跟着;;,这段代码将被求值,并显示结果值。

考虑:

# 4 + 5;;
- : int = 9
# let a = 4 + 5;;
val a : int = 9
#

val只有在我为值引入名称时才会显示。无论哪种情况,都会显示结果值的类型和值本身。

当您看到'a'b时,您看到的是OCaml在无法推断特定类型时推断的类型变量。这个非常最近的问题值得一读。

let用于创建名称绑定(如果您愿意调用它,也可以将其命名为定义),其中绑定将具有名称并将绑定到表达式,当求值时将产生值。

fun用于创建/定义匿名函数,匿名函数是function expression,这意味着函数类型的表达式,在给定其参数的情况下求值将产生一个值,并且该函数表达式本身可以像任何其他常规值一样传递。该函数表达式使用let进行绑定。

例如

let app = fun f -> fun x -> x (f+1)

在本例中,我们定义了一个名称app,它将绑定到右边的fun产生的表达式。fun接受一个形参f,一旦编译器处理完该function expression的体,它的类型将被推断出来。这同样适用于下一个function expression,它接受一个名为x的参数。

通过查看最右边的表达式,它负责生成简化后的值,我们看到f被应用到操作符+上,因此f将从该表达式接收它的类型。然后x (...)需要一个function application,因此x将从该表达式接收它的类型。

我们可以继续对问题中引用的每个表达式应用同样的方法。

本质上,let bindings,lambda expressions,type inference,function application,function expression等…下面是一些我们应该熟悉的话题,以了解正在发生的事情。

最新更新