You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: data/tutorials/language/0it_04_higher_order_functions.md
+35-33
Original file line number
Diff line number
Diff line change
@@ -42,14 +42,14 @@ In OCaml, working with functions quickly becomes second nature. We like to think
42
42
43
43
Take for example a function that says hello to a person by name:
44
44
45
-
```ocaml
45
+
```ocaml
46
46
# let say_hi name = print_string ("Hello, " ^ name ^ "!\n") ;;
47
47
val say_hi : string -> unit = <fun>
48
48
```
49
49
50
50
We can call this function several times, to say "hello" to several people:
51
51
52
-
```ocaml
52
+
```ocaml
53
53
#say_hi "Xavier";;
54
54
Hello, Xavier!
55
55
- : unit = ()
@@ -65,7 +65,7 @@ Hello, Joe!
65
65
66
66
If we wanted to say "hello" to the same person multiple times, we'd just _repeat_ the same line of code.
67
67
68
-
```ocaml
68
+
```ocaml
69
69
# say_hi "Camel";;
70
70
Hello, Camel!
71
71
- : unit = ()
@@ -81,7 +81,7 @@ Hello, Camel!
81
81
82
82
One way we can avoid having to repeat these lines every time is by writing a function to say "hi" 3 times:
83
83
84
-
```ocaml
84
+
```ocaml
85
85
# let say_hi_3_times name =
86
86
say_hi name;
87
87
say_hi name;
@@ -100,7 +100,7 @@ When this happens, it usually means that the function is making certain decision
100
100
101
101
So instead, we will create a function that **let's the caller decide** how many times to say "hi." We do this by requiring a new argument, in this case, `times`:
102
102
103
-
```ocaml
103
+
```ocaml
104
104
# let rec say_many_hi times name =
105
105
if times < 1 then ()
106
106
else begin
@@ -136,11 +136,11 @@ Hello, Camel!
136
136
- : unit = ()
137
137
```
138
138
139
-
Unfortunately, reusing this _repetition_ behaviour isn't so easy because we have hard-coded our call to `say_hi`.
139
+
Unfortunately, reusing this _repetition_ behaviour isn't so easy because we have hard-coded our call to `say_hi`.
140
140
141
141
To make this reusable, we can **let the caller decide** what our function should do:
142
142
143
-
```ocaml
143
+
```ocaml
144
144
# let rec repeat times thing_to_do =
145
145
if times < 1 then ()
146
146
else begin
@@ -153,7 +153,7 @@ val repeat : int -> 'a -> unit = <fun>
153
153
154
154
But what should `thing_to_do` be? Our intuition may be that we can call:
155
155
156
-
```ocaml
156
+
```ocaml
157
157
# repeat 3 (say_hi "Camel");;
158
158
Hello, Camel!
159
159
- : unit = ()
@@ -206,7 +206,7 @@ And we can use `repeat` to recreate our original `say_many_hi`, or to repeat any
206
206
```ocaml
207
207
# let say_many_hi times name = repeat times (fun () -> say_hi name);;
208
208
val say_many_hi : int -> string -> unit = <fun>
209
-
209
+
210
210
# let print_big_space () = repeat 10 print_newline;;
211
211
val print_big_space : unit -> unit = <fun>
212
212
```
@@ -233,13 +233,13 @@ module StringSet :
233
233
|> StringSet.of_list
234
234
|> StringSet.iter fn;;
235
235
val only_once : (string -> unit) -> string list -> unit = <fun>
236
-
236
+
237
237
# let yell_hi name =
238
238
name
239
239
|> String.uppercase_ascii
240
240
|> say_hi;;
241
241
val yell_hi : string -> unit = <fun>
242
-
242
+
243
243
# let call_for_dinner names = only_once yell_hi names;;
244
244
val call_for_dinner : string list -> unit = <fun>
245
245
```
@@ -259,7 +259,7 @@ In the wild, there's certain patterns that repeat over and over again. It's usef
259
259
260
260
### Currying and Uncurrying
261
261
262
-
Since in OCaml all functions really just take one parameter, when you call `add x y`, you're actually calling two functions! `((add x) y)`
262
+
Since in OCaml all functions really just take one parameter, when you call `add x y`, you're actually calling two functions! `((add x) y)`
263
263
264
264
Sometimes it helps to apply _parts_ of a function in different orders, and sometimes it helps to make a function really take all its parameters _at once_.
265
265
@@ -280,7 +280,7 @@ The output function will have type `('a * 'b) -> 'c`. Notice how the arguments `
280
280
281
281
Here's our helper:
282
282
283
-
```ocaml
283
+
```ocaml
284
284
(* [uncurry] takes a function that is normally curried,
285
285
and returns a function that takes all arguments at once. *)
286
286
# let uncurry f (x, y) = f x y;;
@@ -393,14 +393,14 @@ Sometimes it makes sense to curry a function, and sometimes the clearer thing is
393
393
394
394
For example, this is a pipeline with a lot of currying/uncurrying that would most likely be easier to read and maintain if we manually wrote out the wrapper functions:
@@ -449,7 +449,7 @@ But this is not so easy read sometimes, especially as the number of functions gr
449
449
To avoid this we have use the `|>` operator:
450
450
451
451
```ocaml
452
-
let c = foo () |> bar |> baz in
452
+
let c = foo () |> bar |> baz in
453
453
(* ... *)
454
454
```
455
455
@@ -462,7 +462,7 @@ let (|>) x fn = fn x
462
462
463
463
It receives a value `x` and a function `fn` and as soon as it has both, it calls `fn` with `x`. This lets us invert the order and build pipelines that read left-to-right or top-to-bottom instead of inside-out.
464
464
465
-
But what happens when our functions have more than one argument?
465
+
But what happens when our functions have more than one argument?
466
466
467
467
Let's look at an example of string manipulation. We want to get the domain name from an email.
468
468
@@ -500,9 +500,11 @@ email
500
500
;;
501
501
```
502
502
503
-
(** NOTE(@leostera): this example kinda sucks, i'd like one where the use of labels greatly improves the readability but since `ListLabels.nth_opt` doesn't take an argument then we still need that nasty fun flip :( will get back t othis)
503
+
<!--
504
+
NOTE(@leostera): this example kinda sucks, i'd like one where the use of labels greatly improves the readability but since `ListLabels.nth_opt` doesn't take an argument then we still need that nasty fun flip :( will get back t othis)
505
+
-->
506
+
504
507
505
-
506
508
### Iterating
507
509
508
510
We usually think of iteration when we think of looping, and going through collections of things:
@@ -516,7 +518,7 @@ Iterating in OCaml means that if there is a value (or more), we'd like to apply
516
518
517
519
#### Iterating over Lists
518
520
519
-
A list in OCaml is a linked-list that is composed by a head (the first element) and a tail (the rest of the list).
521
+
A list in OCaml is a linked-list that is composed by a head (the first element) and a tail (the rest of the list).
520
522
521
523
We can iterate over lists by pattern matching on then. When doing so, we either get an empty list (`[]`), or we get a pattern with a head and a tail (`n :: rest`). On the branch with a head and a tail, we can directly use the head value and apply a function to it, and then recurse with the tail.
522
524
@@ -560,7 +562,7 @@ let run_if_some opt fn =
560
562
| Some value -> fn value
561
563
| None -> ()
562
564
;;
563
-
565
+
564
566
let run_if_ok res fn =
565
567
match res with
566
568
| Ok value -> fn value
@@ -580,10 +582,10 @@ With either of those functions, we can put together an iterator over maps or set
580
582
581
583
```ocaml
582
584
let iter values collection fn =
583
-
let values : 'a list = values collection in
585
+
let values : 'a list = values collection in
584
586
List.iter fn values
585
587
;;
586
-
588
+
587
589
module StringSet = Set.Make(String);;
588
590
module IntMap = Map.Make(Int);;
589
591
@@ -604,10 +606,10 @@ But some data is _lazy_, and it only lets us access one element at a time. So if
604
606
Lazy sequences in OCaml are represented with the `Seq` module, which has a function called `uncons` to get the next element. This function also returns the new sequence that we can use to get the 2nd element, and so on.
605
607
606
608
```ocaml
607
-
let rec iter seq fn =
609
+
let rec iter seq fn =
608
610
match Seq.uncons seq with
609
611
| None -> ()
610
-
| Some (value, seq2) ->
612
+
| Some (value, seq2) ->
611
613
fn value;
612
614
iter seq2 fn
613
615
;;
@@ -625,7 +627,7 @@ We'll define our tree type to include 2 constructors. One for a leaf node, which
625
627
626
628
```ocaml
627
629
type 'value tree =
628
-
| Leaf of 'value
630
+
| Leaf of 'value
629
631
| Node of 'value tree * 'value
630
632
;;
631
633
```
@@ -648,10 +650,10 @@ Now before we define our iteration function, its important to define what iterat
648
650
For our example, we'll iterate from the top down as we go along:
649
651
650
652
```ocaml
651
-
let rec iter tree fn =
653
+
let rec iter tree fn =
652
654
match tree with
653
655
| Leaf value -> fn value
654
-
| Node (tree2, value) ->
656
+
| Node (tree2, value) ->
655
657
fn value;
656
658
iter tree2 fn
657
659
;;
@@ -749,7 +751,7 @@ If we wanted to implement a sum over the custom tree type we saw in the Iteratin
749
751
750
752
```ocaml
751
753
type 'value tree =
752
-
| Leaf of 'value
754
+
| Leaf of 'value
753
755
| Node of 'value tree * 'value
754
756
;;
755
757
@@ -772,7 +774,7 @@ let rec fold_tree tree fn =
772
774
773
775
But we quickly run into a problem: our `fn` function is meant to combine two items, so in the `Leaf` branch, what is the second item?
774
776
775
-
Folding requires us to define a _zero value_, a starting point for an accumulator, that will be used when the collection or data type is "empty".
777
+
Folding requires us to define a _zero value_, a starting point for an accumulator, that will be used when the collection or data type is "empty".
776
778
777
779
Some data types don't have a good "empty" value. Our tree for example does not. Lists do have an empty list. Options have a `None` constructor. Results' don't have a good "empty" value either.
778
780
@@ -787,7 +789,7 @@ let rec fold_tree tree fn acc =
787
789
```
788
790
789
791
And voila! Our function now types correctly and we can use it to reduce our trees down to any value.
790
-
792
+
791
793
### Sorting
792
794
793
795
Another common behavior usually implemented with higher-order functions is sorting collections.
@@ -955,7 +957,7 @@ type ('input, 'output) operations = {
955
957
<!--
956
958
Comment
957
959
958
-
let f () = 42
960
+
let f () = 42
959
961
let f () = g 42
960
962
961
963
One possible issue with the repeat function used in the intro comes from the fact
@@ -970,7 +972,7 @@ In CS3110, `twice` and `repeat` are pure functions
970
972
```ocaml
971
973
let twice f x = f (f x)
972
974
973
-
let rec repeat f n x = if n = 0 then x else repeat f (n - 1) (f x)
975
+
let rec repeat f n x = if n = 0 then x else repeat f (n - 1) (f x)
974
976
```
975
977
976
978
Trouble with those function is they aren't very realistic, but they are easier to understand (I believe).
0 commit comments