(* val bar = fn : (’a * ’b -> ’b) -> ’b -> ’a list -> ’b *)
fun bar f b nil = b
| bar f b (h::t) = f (h, bar f b t)
This function was given to us with the instructions of explaining what it does. The only further information given are that the parameters are a binary function, a value, and a list. From looking at it, I already know that if the list is nil, it returns the b value, otherwise it applies the binary function to the list head and recurses. I just don't understand how to interpret this line:
(* val bar = fn : (’a * ’b -> ’b) -> ’b -> ’a list -> ’b *)
There are numerous tutorials explaining SML's typing, but I can't find anything in-depth enough to apply to this. Could anyone translate it to English so I know how it works for future reference?
To understand this type sgnature, you need to first understand currying.
A definition like
fun sum a b = a + b
has type int -> int -> int.
It is a function of one variable (an integer) where the return value is itself a function, one which sends ints to ints.
For example, val f = sum 1 assigns to f the function which adds one to its input (in other words, the successor function) so that, e.g., f 5 evaluates to 6.
In practice, such functions are often used like sum 3 4 but what is happening there isn't the passing of 2 values to sum. Rather, the one value 3 is passed, which returns a function, and this returned value is then applied to 4. Thus, sum 3 4 should be parsed as (sum 3) 4 rather than sum (3,4) -- which would be a type error.
Note that this is fundamentally different from something like
fun add (a,b) = a + b
which is a function of two variables, it has type int * int -> int, which is different than sum's type of int -> int -> int. The latter is not syntactic sugar for the former, but instead has a fundamentally different semantics.
When reading something such as int -> int -> int, you should read it as right-associative. In other words, it is the same as int -> (int -> int).
Another thing that is happening with ('a * 'b -> 'b) -> 'b -> 'a list -> 'b is the use of type variables 'a, 'b. This means that the type you are trying to parse is of a higher-order polymorphic function. It 'a and 'b can represent any type.
Putting it all together, a function, f, of type ('a * 'b -> 'b) -> 'b -> 'a list -> 'b is a function which takes as input any function whose type is of the form 'a * 'b -> 'b (a function of two variables whose return type is the type of the second variable). The return value of f is a function of the form 'b -> 'a list -> 'b. This latter is a function which takes an element of type 'b and returns a function which sends 'a lists to objects of type 'b
You could summarize it by saying that f is a curried function which takes a function of type ('a * 'b -> 'b), a value of type 'b, a list of values of type 'a, and returns a value of type 'b. That is accurate enough, but don't slip into thinking of it as equivalent to a function of type
('a * 'b -> 'b) * 'b * 'a list -> 'b
By the way, two of the most useful functions in SML, foldl and foldr have type ('a * 'b -> 'b) -> 'b -> 'a list -> 'b, so this isn't merely an academic exercise. Being able to unpack such type descriptions is a key to being able to use such functions correctly.
I was able to find the solution based on what is apparently called type inferencing. I had never learned this before but
(* val bar = fn : (’a * ’b -> ’b) -> ’b -> ’a list -> ’b *)
is display of argument and return types for the function.
(’a * ’b -> ’b) refers to the first argument function. It requires 2 arguments ('b and 'a) in itself and returns 1 value 'b.
'b refers to the second argument, a value.
'a list refers to a list of values, the third argument in the function.
Finally, the last 'b is the return value.
Related
I want to exploit GADT to implement the type ('a, 'b) liInstr_t in order to hold various types of instructions which are recursively decoded into basic operations (if need be) that then executed. (Eventually, I should construct more abstract types over them but in a mechanical, compositional and scripted fashion.) Unfortunately, I have difficulties associating the locally abstract types from pattern-matching function argument with the alternative concrete return types desired for the GADT.
I believe I'm missing something fundamental or making wrong assumptions though I have looked at the ocaml 4.10.0 manual on locally abstract types and gadts, the Real world Ocaml book, and the responses to similar questions such as here and here. This is because I seem to follow their explanations but cannot somehow apply them to my task.
From the above, I understand that polymorphic type variables annotate functions so that they can take on (be unified with) arbitrary types compatible with their constraints, and that locally abstract types let us have different types along alternative paths through a pattern-matching expression, say. Also, the local abstract types cannot be unified but can be refined to concrete types compatible with GADT result types. As such, GADTs can recurse over polymorphic types, and aggregate multiple result types into a single sum type.
I deliberately let the type ('a, 'b) liInstr_t have two type variables (so I can add more later), and its variants capture various constraint formats and scenarios I may have to use together.
type
liLabel_t = string (* Instruction name (label) *)
and
context_t = string (* TODO: execution context *)
and
'a context_list_t = 'a list
and
'a liChooser_t = 'a -> int (* get index of i-th list entry *)
and
('a, 'b) liInstr_t =
LiExec: 'a -> ('a, 'b) liInstr_t (* executable operation *)
| LiExecTRY: ('a, _) liInstr_t (* Ignore: Experiment on GADT *)
| LiLab: liLabel_t -> ('a, 'b) liInstr_t (* instruction label *)
| LiLabTRY: (liLabel_t, _) liInstr_t (* Ignore: Experiment on GADT *)
| LiSeq: 'a liChooser_t * 'b list -> ('a, 'b) liInstr_t (* sequence *)
| LiAlt: 'a liChooser_t * 'b list -> ('a, 'b) liInstr_t (* choice *)
| LiLoop: 'a liChooser_t * 'b list -> ('a, 'b) liInstr_t (* loop *)
| LiName: 'a liChooser_t * liLabel_t * 'b context_list_t ->
('a, 'b) liInstr_t (* change context *)
| Err_LiInstr: ('a, 'b) liInstr_t (* error handling *)
| Nil_LiInstr: ('a, 'b) liInstr_t (* no action *)
After experimenting, the sample function used is:
let ft1: type b c. (b, c) liInstr_t -> b = function
(* *) | LiExec n -> n
(* *) | LiExecTRY -> "4"
(* *) | LiLab s -> "LiLab"
(* *) | LiLabTRY -> "LiLabTRY"
(* *) | LiSeq (f, il) -> "LiSeq"
(* *) | LiAlt (f, il) -> "LiAlt"
(* *) | LiLoop (f, il) -> "LiLoop"
(* *) | LiName (f, il, ic) -> "LiName"
(* *) | Err_LiInstr -> "Err_LiInstr"
(* *) | Nil_LiInstr -> "Nil_LiInstr"
;;
and it gave the error:
Line 3, characters 22-25:
3 | (* *) | LiExecTRY -> "4"
^^^
Error: This expression has type string but an expression was expected of type
b
I still got errors when I changed the function annotation (and typing), or commented out some alternatives in the function pattern matching and the GADT type variants. Some of the errors (elided for brevity) were obtained as follows:
Using an extra locally-typed variable:
let ft1 : type b c d. (b, c) liInstr_t -> d = function ...
2 | (* *) | LiExec n -> n
^
Error: This expression has type b but an expression was expected of type d
Using only polymorphic type variables:
let ft1: 'b 'c. ('b, 'c) liInstr_t -> 'b = function ...
Error: This definition has type 'c. (liLabel_t, 'c) liInstr_t -> liLabel_t
which is less general than 'b 'c. ('b, 'c) liInstr_t -> 'b
My questions then are the following:
How can we capture and use the abstract types identified with alternative paths? A locally abstract type should bind (or be refined) to compatible concrete type(s) for values found in the resulting expression, or can be ignored, right? Ignoring recursion, this example:
let rec eval : type a. a term -> a = function
| Int n -> n (* a = int *)
| Add -> (fun x y -> x+y) (* a = int -> int -> int *)
| App(f,x) -> (eval f) (eval x)
(* eval called at types (b->a) and b for fresh b *)
on expression evaluation in the ocaml manual seems to suggest that is the case, at least for a 1-parameter GADT type. So, why aren't my types b and c not suitably bound (or refined) in the return type? And if they are binding (or being refined), which should bind to abstract type b and which to c, if at all? How can I find values for my return type so they can correctly associate with the abstract, value-less types reaching them. For, there is seems no way to obtain a result that has the type b in my first error above!
Why am I forced to have the same result type for the alternative paths to succeed (string type in my examples) whereas all possible result types of the GADT should be admissible. In this regard, the first variant (LiExec n -> n) seemed forced to have type string! Also, the abstract types and polymorphic variables along execution paths seem irrelevant to the result type!
I could not reproduce it but at one point, making the first variant LiExec n -> 4 seemed to require integer return values from all alternative pattern matches. If indeed this is the case, why should abstract types on alternative paths require values from the same non-GADT return type? (This behaviour is of non-polymorphic types, right?)
To work around incomprehensible issues on polymorphic types and locally abstract types, is there a simple way to mix them in a constraint? Various permutations to mix them always seem to result in a syntax error. e.g.:
let ft1: (type d) 'b 'c. ('b, 'c) liInstr_t -> d = function
^^^^
Error: Syntax error
Suppose we have the following GADT:
type _ simple_gadt =
| Con : 'a -> 'a simple_gadt
The type signature of Con can be understood as ('a : Type) -> 'a -> 'a simple_gadt (not real OCaml syntax); in other words, it takes a type as its first argument, and the rest of the type is dependent on this input type. The client provides the type; for example:
let value : int simple_gadt = Con 0
Implicitly, you can understand this definition as really meaning let value = Con(type int, 0), where the type is given as an argument (again, not real OCaml syntax).
When you write a function that takes a 'a simple_gadt as an argument, you don't know what 'a is. 'a is said to be an "existential type" provided by the caller of the function. Consider the following function:
let f (type a) (param : a simple_gadt) : a = match param with
| Con x -> x
The type of f is 'a . 'a simple_gadt -> 'a. A client can evaluate f (Con 0) and get back 0, of type int. A client can also evaluate f (Con true) and get back true, of type bool. The definition of the function has no control over what the actual type 'a is; only the caller does.
Suppose we attempt to define:
let g (type a) (param : a simple_gadt) : a = match param with
| Con _ -> ""
One would be able to evaluate g (Con 0) and get back "", a string, but based on the type of Con 0, the output of the function should be an int. This is clearly a type error, so g has an ill-typed definition, and the compiler rightfully rejects it. Likewise, your definition
let ft1: type b c. (b, c) liInstr_t -> b = function
(* ... *)
(* *) | LiExecTRY -> "4"
(* ... *)
is ill-typed because it assumes that b is string, while b could be any type that the caller provides. It looks like you have other similar type errors because you are attempting to pick more specific types for the existential types.
If the caller can choose any type, how can one use GADTs to "refine" the type variable to a more concrete type? The only way to do this is through the information that the caller provides.
Consider the following type definition:
type _ term =
| Abs : ('a -> 'b) -> ('a -> 'b) term
| App : ('a -> 'b) term * 'a term -> 'b term
| Bool : bool -> bool term
In a GADT, each constructor can make the type parameters more specific. Therefore, by pattern matching against each constructor, a function can refine the existential type parameter.
Consider this function on the GADT defined above:
let rec eval : 'a . 'a term -> 'a =
fun (type a) (term : a term) : a ->
match term with
| Abs f -> f
| App(f, x) -> (eval f) (eval x)
| Bool b -> b
In the Abs f case, Abs f is known to have type ('a -> 'b) term for some 'a and 'b by the definition of Abs. Similar reasoning applies for the App(f, x) and Bool b cases.
What's a universally quantified type from the caller's perspective (i.e. the caller can pick any type) must be an existentially quantified type from the callee's perspective (i.e. the callee must work with some fixed arbitrary type that the caller provides).
In brief, the type li_Instr_t as defined is not an interesting GADT and it can be rewritten to the strictly equivalent ADT
type ('a, 'b) liInstr_t =
| LiExec of 'a
| LiExecTRY
| LiLab of liLabel_t
| LiLabTRY
| LiSeq of 'a liChooser_t * 'b list
| LiAlt of 'a liChooser_t * 'b list
| LiLoop of 'a liChooser_t * 'b list
| LiName of 'a liChooser_t * liLabel_t * 'b context_list_t
| Err_LiInstr
| Nil_LiInstr
because the type declaration never introduces equations (or existential quantifications) between the result type and the constructor of the GADT.
If we look at a simple example for GADT:
type ('elt,'array) compact_array =
| String: (char, String.t) compact_array
| Float_array: (float, Float.Array.t) compact_array
| Standard: ('a, 'a array) compact_array
let init: type elt array.
(elt,array) compact_array -> int -> (int -> elt) -> array =
fun kind n f -> match kind with
| String -> String.init n f
| Float_array -> Float.Array.init n f
| Standard -> Array.init n f
The difference is that the constructor String constrains the type of compact_array to be (char,string) compact_array. Thus, when I observe String in the pattern matching above, I can introduce the equation
elt=char and array=string in the branch String and use those equation locally . Similarly, after observing the constructor Float_array in the pattern matching, I can work with the equation elt=float and array=Float.Array.t inside the corresponding branch.
Contrarily, with the definition of liInstr_t as it stands, observing a constructor of a value of type ('a,'b) liInstr_t brings no information on the type ('a,'b) liInstr_t. Consequently, the function ft1 of type type a b. (a,b) liInstr_t -> b is is promising to return a float array when called with ft1 (LiExecTRY:('a,float array) li_Instr_t). More generally, a function of type a b. (a,b) liInstr_t -> awhere no constructor impose a constraint onbis necessarily returning some value of typebthat was contained inside(a,b) liInstr_t` (or is not returning).
Using that knowledge, we can update your type liInstr_t to make the function ft1 works by adding the equations corresponding to the expected return type for ft1 to the definition of the type:
type liLabel_t = string
and context_t = string
and 'a context_list_t = 'a list
and 'a liChooser_t = 'a -> int
and ('a, 'b, 'ft1) liInstr_t =
| LiExec: 'a -> ('a, 'b,'a) liInstr_t (* ft1 returns the argument of LiExec *)
(* ft1 returns a string in all other cases *)
| LiExecTRY: ('a, 'b, string) liInstr_t
| LiLab: liLabel_t -> ('a, 'b, string) liInstr_t
| LiLabTRY: (liLabel_t, 'b, string) liInstr_t
| LiSeq: 'a liChooser_t * 'b list -> ('a,'b, string) liInstr_t
| LiAlt: 'a liChooser_t * 'b list -> ('a,'b, string) liInstr_t
| LiLoop: 'a liChooser_t * 'b list -> ('a,'b, string) liInstr_t
| LiName: 'a liChooser_t * liLabel_t * 'b context_list_t ->
('a,'b, string) liInstr_t
| Err_LiInstr: ('a, 'b, string) liInstr_t
| Nil_LiInstr: ('a, 'b, string) liInstr_t
and now that we have the right equation in place, we can define ft1 as:
let ft1: type a b c. (a, b, c) liInstr_t -> c = function
| LiExec n -> n
| LiExecTRY -> "4"
| LiLab s -> "LiLab"
| LiLabTRY -> "LiLabTRY"
| LiSeq (f, il) -> "LiSeq"
| LiAlt (f, il) -> "LiAlt"
| LiLoop (f, il) -> "LiLoop"
| LiName (f, il, ic) -> "LiName"
| Err_LiInstr -> "Err_LiInstr"
| Nil_LiInstr -> "Nil_LiInstr"
which typechecks without any error.
I have a heterogeneous list and a function on them
type ('ls,'tl) hlist =
| Nil : ('a,'a) hlist
| Cons: 'a * ('l, 't) hlist -> ('a -> 'l, 't) hlist
let rec headlist l =
match l with
| Cons (h, t) -> Cons (head h, headlist t)
| Nil -> Nil
and would like to traverse a hlist of lists of different types, and build a list of the heads of each list. The idea is something like this:
headlist Cons( [1,2,3], Cons( ['a','b'], Cons( [true,false], Nil )))
= Cons( 1, Cons( 'a', Cons( true, Nil)))
However, I encounter a type error.
Error: This expression has type ('a, 'b list -> 'a) hlist
but an expression was expected of type
('b list -> 'a, 'b list -> 'a) hlist
The type variable 'a occurs inside 'b list -> 'a
I don't understand the type error. What is it saying? Am I trying to do something impossible?
Your problem start with the fact that it is not possible to write a type for the headlist function that you have in mind. Since it is in general necessary to write explicitly the type of functions manipulating GATDs, it is good practice to start writing this type, to check that one can write the type; and only remove it afterward in the rare cases where it is possible to elide the explicit type annotations.
The root of the issue here is that heterogeneous lists are much more rigid than normal lists. In particular, depending on the operations needed on such lists, it is frequent to have to tailor specialized heterogeneous list types. For instance, with the classical heterogeneous list:
type void = |
module Hlist = struct
type 'a t =
| []: void t
| (::): 'a * 'l t -> ('a -> 'l) t
let head(x::_) = x
let rec length: type a. a t -> int = function
| [] -> 0
| a :: q -> 1 + length q
end
it is impossible to express the condition: all elements of the heterogeneous list are heterogeneous lists with at least one element themselves. However, it is possible to define another list type that does enforce this condition:
module Hlist_of_nonempty_hlist_0 = struct
type 'a t =
| []: void t
| (::): (('h -> 'more) as 'a) Hlist.t * 'l t -> ('a -> 'l) t
end
With this new list type, I can compute the length of all nested lists:
let rec map_length: type a. a Hlist_of_nonempty_hlist_0 t -> int list = function
| [] -> []
| a :: q -> Hlist.length a :: map_length q
However, I can still not apply head to all elements, because the types of the head are not easily accessible. One option is to store those types directly in the type of Hlist_of_nonempty_hlist:
module Hlist_of_nonempty_hlist = struct
type ('a,'b) t =
| []: (void,void) t
| (::):
(('h -> 'more) as 'a) Hlist.t * ('l,'hl) t
-> ('a -> 'l, 'h -> 'hl) t
end
and with this specialized heterogeneous list type, writing the type of map_head becomes straightforward:
let rec map_head:
type l hl. (l, hl) Hlist_of_nonempty_hlist.t -> hl Hlist.t
= function
| [] -> []
| (a::_) :: q -> a :: map_head q
But this is a lot of design work on the type for one function. And going further and trying to write any generic functions over heterogeneous lists generally require a lot of polymorphic records and functors.
I don't think there's a type that describes the function you want. You want to say that the input is an hlist all of whose heterogeneous types are lists. I don't see a way to say that, which suggests to me that you can't have a function like this.
However, I have been wrong many times, and GADTs are something I'm particularly unsteady about.
If I understand correctly, your function headlist is supposed to have type ('a list -> 'b list -> ... -> 'z, 'z) hlist -> ('a -> 'b -> ... > 'z, 'z) hlist. I do not think there is a single OCaml type that covers all the possible arities. So, the compiler looks for a much simpler type, hence the strange error message.
I have to write a function to remove elements from a lazy list. Indexes of elements to be removed are in list xs.
I don't know where I should sort xs? When I try in this way I get "Error: This expression has type...".
type 'a llist = LNil | LCons of 'a * (unit -> 'a llist)
let rec remove xs ll =
let rec helper =
function
| (_, i, LNil) -> LNil
| (h::t, i, LCons(x, xf)) -> if h = i then helper (t, (i + 1), xf())
else LCons(x, fun() -> helper (h::t, (i + 1), xf()))
| ([], i, LCons(x, xf)) -> LCons(x, xf)
in helper (List.sort xs, 0, ll);;
List.sort from OCaml standard library has the following interface:
val sort : ('a -> 'a -> int) -> 'a list -> 'a list
That means, that it accepts a function and a list. The function should have type 'a -> 'a -> int, i.e., it takes two elements of arbitrary type 'a and returns a value of type int that defines the mutual order of the arguments. The second argument is a list of values, where each value has type 'a. A usual invocation of the sort function is:
List.sort compare [2;1;4;3]
So with this knowledge we can tackle with your program:
You invoke List.sort on xs this has two consequences:
type inference system concludes that xs is a function of type 'a -> 'a -> int.
The result of List.sort xs is a function of type 'a list -> 'a list. This is because, List.sort requires two arguments, but you provided only one.
(List.sort xs) is a function that takes a list & returns a list - as xs is supposed to be the function that sorts the element of the list; you miss to pass a list as arg.
...whereas a list is expected.
Write an Ocaml function list_print : string list -> unit that prints all the strings in a list from left to right:
So Lets say I've got an Ocaml function list_print: string list -> unit that prints all the strings in a list from left to write. Now the correct solution is:
let list_print lst = List.fold_left (fun ( ) -> fun s -> print_string s) () lst;;
But When writing my solution, I wrote it as such:
let list_print lst = List.fold_left (fun s -> print_string s) () lst;;
But this gave me
Error: This expression has type unit but an expression was expected of type 'a -> string
Why is it that I need that first parameter fun() -> before the fun s? I'm still new to Ocaml so this type system is quite confusing to me
The purpose of fold_left (and fold_right) is to accumulate a value as you go along. The extra parameter is this accumulated value.
You can use List.iter for your problem. It doesn't accumulate a value.
You could think of List.iter as a version of List.fold_left that accumulates values of type unit. And, in fact, you could implement it that way:
let iter f = List.fold_left (fun () a -> f a) ()
The point (as always with unit) is that there's only one value of the type, so it represents cases where the value isn't interesting.
You want to use List.fold_left, that's fine, but you should start by reading the documentation for that function. The official documentation is quite short:
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
List.fold_left f a [b1; ...; bn] is f (... (f (f a b1) b2) ...) bn.
The first thing is the type of that function. The type is
('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
In other words, the function fold_left has three arguments and one result value. The first argument has type ('a -> 'b -> 'a). The second argument has type 'a. The third argument has type 'b list. The result value of the function has type 'a.
Now, in your case, you want to print the strings. So you do not actually need any result value, you need a side effect. However, in OCaml all functions must have a result value. So you use the empty value, (), which has type unit. Therefore, the type parameter 'a will be equal to unit in your case.
The type parameter 'b is string because you are required to work on the list of strings.
Therefore, in your case the function fold_left must have the type
(unit -> string -> unit) -> unit -> string list -> unit.
The first argument of fold_left must have the type unit->string->unit. In other words, it must be a function with two arguments, first argument is the empty value, i.e. (), the second argument a string. So the first argument to fold_left must be a function of this kind,
fun x y -> ...
where x must be of type unit and y of type string. Since x is going to be always equal to (), it is not necessary to write this argument as a variable x, instead we can simply write () or even the dummy argument _. (The syntax fun x -> fun y -> ... gives the same function as fun x y -> ....)
Now you can begin to figure out how fold_left works. Since this is obviously a homework question, I will leave this task to you.
Write any Ocaml function whose type is ('a -> 'b) list -> 'a -> 'b list
('a -> 'b) list is the part that confuses me the most. I'm new to OCaml and having a hard time understanding how to write a function to get a specific datatype type.
# let int x = x+1;;
# let fcn = [int; int];;
So I'm passing a function a function and a variable. I'm going to take that variable an add it to each element of the list and return the list?
('a -> 'b) means a function which goes from type 'a to type 'b. Basically you need to make a function which takes a list of functions that take 'a and return 'b, plus a specific 'a value, and which returns a list of 'b values (probably by applying each function of the list of functions to the specific 'a value).
As this is homework, I will not provide you with a complete solution. But, as a hint, I would suggest that you take a look at this implementation of the familiar map function:
let rec map f = function
| [] -> []
| x :: xs -> f x :: map f xs
It has type ('a -> 'b) -> 'a list -> 'b list which means that it takes as its first argument a function that takes values of some type 'a to values of some type 'b, as its second argument a list of elements of type 'a, and that it produces a list of elements of type 'b. It proceeds by pattern matching on the argument list and, recursively applying the function (f) to every element x of the list.
Now have a look at the type of the function that you have to write? What does it tell you about the required behaviour of that function? Keeping the implementation of the map function in mind, how would you write your function?
('a -> 'b) list -> 'a -> 'b list
This means that your function has two parameters
A list of ('a -> 'b) which represents a function taking an element of type 'a as a parameter and returning an element of type 'b. As you can see, these types are abstract, so they could be of any types for instance (int -> int) or (int -> float) etc...
An elements of types 'a. Notice that this type must be the same as the parameter of your function.
So you'll build the resulting list with the element you give as a parameter.
Here is a little example:
let action l a =
let rec todo l res =
match l with
| [] -> res
| h :: t -> todo t res#[h a] in
todo l []
so here, any function of type int -> int will be accepted. The same thing goes for any other type as long as you don't mix them with other types.
let rec func f a = match f with (* ( 'a->'b ) list -> 'a -> 'b list *)
|[]->[]
|x::lr -> x a :: func lr a;;
that may help ! it works fine
1 - So as we know , ocaml create the type of our function line by line
2 - in this function we have two arguments f and a
3 - ( 'a->'b ) list : for f
4 - 'a : for a ! how ocaml did that ? listen !
5 - when we matched f with [ ] 'blank list' ocaml release that is a list (****)list but doesn't know what contains yet in the last line of the code he will do ok ? nice !
- here we are in the last line of the code and we have only f of type list -
6 - x :: lr means we sort the first element of the element that is matched before : f and we add a here ocaml gives a type for a and for the list elements which is matched : f as first elements ocaml gives them types from 'a to 'z so here we have ('a->'b) list for f and 'a for a
-here we have f of type : ('a->'b) list , and a of type : 'a
7 - the result of this function 'b list so it's up to you to answer in comment ! :D thank you