[英]Type in functional programming (OCaml)
I am learning functional programming and I am not understanding the types in OCaml, and I have not found anything real helpful. 我正在学习函数式编程,并且不了解OCaml中的类型,并且我还没有发现真正有用的东西。
I have this code: 我有以下代码:
let rec map2 f l1 l2 =
match (l1,l2) with
([], _) -> []
| (_, []) -> []
| (x::l1s,y::l2s) -> (f x y) :: map2 f l1s l2s;;
let pick n m =
if n > m then (fun b x -> if b then x+1 else x*2)
else (fun b x -> if b then x+2 else x*4);;
map2 (pick 7 9) [true;false] [3;4;5;6];;
What I find difficult to understand the steps to know the type of this kind of functions (map2, pick). 我发现很难理解了解此类功能类型的步骤(map2,pick)。
I know a little bit how signature works, the right associative property and that the symbol " ' " refers to a generic type. 我知道一点点的签名是如何工作的,正确的关联属性和符号“'”指的是一个泛型类型。
solution: 解:
pick: 'a -> 'a -> bool -> int -> int = <fun>
map2: ('a->'b->'c) -> 'a list -> 'b list -> 'c list
I don't understand why bool -> int and why in map bool isn't in the function parameter. 我不明白为什么bool-> int以及为什么map bool不在函数参数中。
Any reference to books, link, is welcomed 任何对书籍,链接的引用都欢迎
pick: 'a -> 'a -> bool -> int -> int = <fun>
map2: ('a->'b->'c) -> 'a list -> 'b list -> 'c list
What you see here is a process in functional programming called currying . 您在这里看到的是函数编程中称为currying的过程 。 To make sense of this, let's consider a simpler example.
为了理解这一点,让我们考虑一个更简单的示例。 Let's say you have a function
f
that takes 2 arguments X
and Y
, and output Z
. 假设您有一个函数
f
,它接受2个参数X
和Y
,并输出Z
How we usually write this is 我们通常的写法是
f(X, Y) = Z
Let's see this in a different way - we have a function f
, and if we give it X
, and then Y
, it will gives us Z
. 让我们以不同的方式看待这一点-我们有一个函数
f
,如果给它X
,然后给Y
,它将给我们Z
What would happen if we only give f
1 argument, X
? 如果我们仅给
f
1自变量X
会发生什么? Let's test it out! 让我们测试一下!
let plus a b = a + b;;
This code defines a function plus
, which takes 2 arguments a
and b
and returns their sum. 这段代码定义了一个
plus
函数,该函数接受2个参数a
和b
并返回它们的总和。 If you type plus 1 1;;
如果键入
plus 1 1;;
into utop
, it will give you 2
. 进入
utop
,它将给您2
。 Now, the output when you type 现在,键入时的输出
plus 1;;
is 是
- : int -> int = <fun>
This means plus(1)
actually produces a FUNCTION that takes an int
and output an int
! 这意味着
plus(1)
实际上会产生一个函数,该函数接受一个int
并输出一个int
! Wait a minute, initially we have a function that produces integer, and suddenly the same function is producing ... FUNCTION? 等一下,最初我们有一个产生整数的函数,突然之间,同一函数产生了...功能? What's going on?
这是怎么回事?
The key idea here is to think of a function as a process that consumes the arguments one by one . 这里的关键思想是将函数视为一个过程,该过程一个接一个地消耗参数 。 In the spirit of this, the function
plus
above is like a process that consumes 2 arguments: if you give it just 1 argument, it would stall and wait for the 2nd argument. 在这种精神的功能
plus
上面就是这样消耗2个参数的方法:如果你给它只是1周的说法,这将拖延和等待第二个参数。 And this stalled process is exactly similar to a process that consumes 1 argument: give it the remaining ingredient and it would start grinding to give you the expected output. 这个停顿的过程与消耗1个参数的过程完全相似:给它剩余的成分,它将开始磨碎以提供预期的输出。
To see how this perspective can help you make sense of the obscure way in which function signature is written in your example, let's look at the function pick
: 为了了解这种观点如何帮助您理解示例中编写函数签名的晦涩方式,让我们看一下函数
pick
:
let pick n m =
if n > m then (fun b x -> if b then x+1 else x*2)
else (fun b x -> if b then x+2 else x*4);;
pick
takes in 2 arguments, n
and m
, and output a function f
that takes in 2 arguments b
and x
. pick
接受2个参数n
和m
,并输出函数f
接受2个参数b
和x
。 The definition of f
depends on a comparison. f
的定义取决于比较。 If n > m
, then it outputs a function fun bx
whose definition is if b then x+1 else x*2
. 如果
n > m
,则它输出一个函数fun bx
其定义是if b then x+1 else x*2
。 Else, it outputs a function fun bx
whose definition is if b then x+2 else x*4
. 否则,它输出一个函数
fun bx
其定义是if b then x+2 else x*4
。
If we were to write a rough signature for pick
based on the above understanding, it would be something like: 如果我们根据上述理解为
pick
写一个粗略的签名,它将类似于:
pick: (n, m) -> (function f that takes in b and x, and output some number)
In light of our understanding of currying, pick
is like a process that consumes n
, and then m
. 根据我们对curring的理解,
pick
就像一个消耗n
,然后消耗m
。 So the signature can be written in this way too: 所以签名也可以这样写:
pick: n -> m -> (function f that takes in b and x, and output some number)
Oh hey, this function f
can also be thought of as a process that consumes b
and then x
, so we can also write: 哦,嘿,这个函数
f
也可以看作是一个先消耗b
然后再消耗x
的进程,所以我们也可以这样写:
pick: n -> m -> (b -> x -> some number)
which looks strikingly similar to: 看起来非常类似于:
pick: 'a -> 'a -> bool -> int -> int = <fun>
Now, as of how the heck does OCaml know that b
is supposed to be bool
, and x
and some number
is supposed to be int
, OCaml has a featured called type inference . 现在,关于OCaml到底是怎么知道
b
应该是bool
,而x
和some number
应该是int
,OCaml具有一个称为类型推断的功能 。 Basically speaking, the compiler looks at the operations you perform on the variables and try to make a guess of their types. 基本上,编译器会查看您对变量执行的操作,并尝试猜测它们的类型。 Eg I see
if b
in the code, so probably b
should be a bool
. 例如,我查看代码中的
if b
,所以b
可能应该是bool
。
In summary , that obscure way in which the function signature is written is called currying , and how OCaml knows b
is a bool
is through a feature called type inference in OCaml. 总而言之 ,写函数签名的晦涩方式被称为currying ,而OCaml如何知道
b
是bool
是通过OCaml中的一种称为类型推断的特征来实现的。 This should makes it easier for searching. 这应该使搜索更容易。
If you look at pick then you see that it takes 2 argument and then returns a function. 如果查看pick,则会看到它接受2个参数,然后返回一个函数。 What it returns is (same for the other fun):
它返回的是(与其他乐趣相同):
(fun b x -> if b then x+1 else x*2)
The "if" construct is of the form if <bool> then <'a> else <'a>
. “ if”构造的形式为
if <bool> then <'a> else <'a>
。 So b must be bool while x can still be 'a. 所以b必须是布尔值,而x仍然可以是'a。 Going deeper there is
x+1
. 更深入的是
x+1
。 So x must be int
and the result is int
too. 所以x必须
int
,结果是int
了。
So the above function is bool -> int -> int
and hat is shown in the type of pick. 因此,上面的函数是
bool -> int -> int
并且在pick类型中显示hat。
As for map2: The map2 function can work with any function of the form 'a -> 'b -> 'c
as first argument. 至于map2:map2函数可以使用形式为
'a -> 'b -> 'c
任何函数作为第一个参数。 In your example you pass bool -> int -> int
but this does not limit map2 to that type. 在您的示例中,您传递了
bool -> int -> int
但这并不将map2限制为该类型。 Your code could continue with 您的代码可以继续
let pick_f n m =
if n > m then (fun b x -> if b then x +. 1. else x *. 2.)
else (fun b x -> if b then x +. 2. else x *. 4.);;
map2 (pick_f 7 9) [true;false] [3.;4.;5.;6.];;
pick_f: 'a -> 'a -> bool -> float -> float = <fun>
The same map2 function is used but here with bool -> float -> float
as type of the first argument. 使用相同的map2函数,但此处将
bool -> float -> float
作为第一个参数的类型。 The map2 function is polymorphic (what you called generic) and can work with many types. map2函数是多态的(您称为泛型),可以使用许多类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.