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键。更具体地说,我不明白let
和val
之间的关系。
例如,第一个:
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
等…下面是一些我们应该熟悉的话题,以了解正在发生的事情。