Is it best to always eta-expand as a fool-proof rule of thumb in OCaml ?
type 'x recordx = { x : int }
type 'y recordy = { y : int }
let rec ok : type n a. n recordx -> a recordy -> int =
fun algebrah x -> ok algebrah x
let rec ok : type a. a recordy -> int =
fun x ->
let go : type b. b recordy -> int = ok in
go x
let rec ok : type n a. n recordx -> a recordy -> int =
fun x y ->
let go : type b. b recordy -> int = fun y -> ok x y in
go y
let rec ok : type n a. n recordx -> a recordy -> int =
fun x y ->
let go = ok x in
go y
(* This definition has type
'b recordy -> int which is less general than
'b0. 'b0 recordy -> int
*)
let rec ko : type n a. n recordx -> a recordy -> int =
fun x y ->
let go : type b. b recordy -> int = ko x in
go y
The relaxed value restriction classifies the eta-expanded form fun y -> f x y as a value that can thus be generalized by let bindings, contrarily to the non-value f y. See https://ocaml.org/manual/polymorphism.html#s%3Aweak-polymorphism .
Moreover, in a eager language like OCaml, eta-expansion does change the semantics of functions. Consider
let print x =
Format.printf "x=%s#." x;
fun y -> Format.printf "y=%s#."
and
let print1 = print "x"
let print2 y = print "x" y
Thus, eta-expansion is a semantic-changing transformation rather than a "foolproof" transformation.
Related
When declaring a function, I've 3 different ways:
let f x = ...
let f = (fun x -> ...)
let f = function
| ... -> (pattern matching)
It's this last one that I don't fully understand how it works.
I was doing a function that, considering a list (we'll assume it has integers in it but could be anything), reverses it, pretty basic, but with a complexity of O(n). After struggling for an hour at least I check the answer, and it is written like this:
let reverse lst =
let rec aux acc = function
| [] -> acc
| hd :: tl -> aux (hd :: acc) tl
in
aux [] lst
I thought that using the key word function was just another way of doing patter matching, but when I do this:
let reverse lst =
let rec aux acc =
match aux with
| [] -> acc
| hd :: tl -> aux (hd :: acc) tl
in
aux [] lst
It doesn't work, and idk why. On top of that, why can we add tl at the end of the first function? Isn't aux a single argument function?
There are a few problems with this question. First, the code you give as the solution for reverse is not valid OCaml. It matches aux (which is a function) against list patterns. Most likely aux was supposed to be acc. But even so it doesn't seem right because it should have two arguments (the accumulated result and the input that still needs to be processed).
Second, your two code examples are the same. You seem to be saying that one works and one doesn't work. That doesn't make sense since they're the same.
IMHO you need to rewrite the question if you want to get a helpful answer.
Ocaml uses currying, which means that a two-argument function is the same thing that a function whose return value is a function.
To define a two-argument function, you can combine all the ways you know of creating one-argument functions:
let f x y = x + y
let f x = (fun y -> x + y)
let f x = function
| y -> x + y
let f = (fun x -> (fun y -> x + y))
let f = function
| x -> function
| y -> x + y
let f x = (let g y = x + y in g)
etc, etc.
All these definitions for f lead to the same result:
val f : int -> int -> int = <fun>
# f 3 4;;
- : int = 7
Note that the signature of f is:
val f : int -> int -> int = <fun>
If we added parentheses to better understand this signature, it would be this:
val f : int -> (int -> int) = <fun>
Meaning that f is a one-argument function whose return value is a one-argument function whose return value is an int.
Indeed, if we partially apply f:
# f 3;;
- : int -> int = <fun>
# let add_three = f 3;;
val add_three : int -> int = <fun>
# add_three 4;;
- : int = 7
The code you give at the end of your question is wrong. It's most likely intended to be this:
let reverse lst =
let rec aux acc l =
match l with
| [] -> acc
| hd :: tl -> aux (hd :: acc) tl
in
aux [] lst;;
val reverse : 'a list -> 'a list = <fun>
# reverse [1;2;3;4;5];;
- : int list = [5; 4; 3; 2; 1]
I have the following peano number written with GADTs:
type z = Z of z
type 'a s = Z | S of 'a
type _ t = Z : z t | S : 'n t -> 'n s t
module T = struct
type nonrec 'a t = 'a t
end
type 'a nat = 'a t
type e = T : 'n nat -> e
The following function to decode a 'a nat (or 'a t) into the number it encoded, works:
let to_int : type n. n t -> int =
let rec go : type n. int -> n t -> int =
fun acc n -> match n with Z -> acc | S n -> go (acc + 1) n
in
fun x -> go 0 x
but if I try to rewrite it almost exactly the same this way:
let to_int2 (type a) (a: a nat) : int =
let rec go (type a) (acc : int) (x : a nat) : int =
match x with
| Z -> acc
| S v -> go (acc + 1) v
in
go 0 a
I get a scope error. What's the difference between the two functions?
138 | | S v -> go (acc + 1) v
^
Error: This expression has type $0 t but an expression was expected of type
'a
The type constructor $0 would escape its scope
The root issue is polymorphic recursion, GADTs are a red herring here.
Without an explicit annotation, recursive functions are not polymorphic in their own definition.
For instance, the following function has type int -> int
let rec id x =
let _discard = lazy (id 0) in
x;;
because id is not polymorphic in
let _discard = lazy (id 0) in
and thus id 0 implies that the type of id is int -> 'a which leads to id having type int -> int.
In order to define polymorphic recursive function, one need to add an explicit universally quantified annotation
let rec id : 'a. 'a -> 'a = fun x ->
let _discard = lazy (id 0) in
x
With this change, id recovers its expected 'a -> 'a type.
This requirement does not change with GADTs. Simplifying your code
let rec to_int (type a) (x : a nat) : int =
match x with
| Z -> 0
| S v -> 1 + to_int v
the annotation x: a nat implies that the function to_int only works with a nat, but you are applying to an incompatible type (and ones that lives in a too narrow scope but that is secondary).
Like in the non-GADT case, the solution is to add an explicit polymorphic annotation:
let rec to_int: 'a. 'a nat -> int = fun (type a) (x : a nat) ->
match x with
| Z -> 0
| S v -> 1 + to_int v
Since the form 'a. 'a nat -> int = fun (type a) (x : a nat) -> is both a mouthful and quite often needed with recursive function on GADTs, there is a shortcut notation available:
let rec to_int: type a. a nat -> int = fun x ->
match x with
| Z -> 0
| S v -> 1 + to_int v
For people not very familiar with GADTs, this form is the one to prefer whenever one write a GADT function. Indeed, not only this avoids the issue with polymorphic recursion, writing down the explicit type of a function before trying to implement it is generally a good idea with GADTs.
See also https://ocaml.org/manual/polymorphism.html#s:polymorphic-recursion , https://ocaml.org/manual/gadts-tutorial.html#s%3Agadts-recfun , and https://v2.ocaml.org/manual/locallyabstract.html#p:polymorpic-locally-abstract .
I am new to SML and I am trying to practice in SML type reference.I am trying to deduct the below types:
a)fun add42 x =x+42
b)fun comp F G = let fun C x = G(F(x)) in C end
c)fun compA42 x = comp add42 x
d)val foo = compA42 add42
e)fun compCompA42 x = comp compA42 x
I think the solutions for the first four is:
a)int->int
b)(a->b)->(b->c)->a->c
c)(int->a)->int->a
d)int->int
But I am a little bit confused about the last one.
Is there any hint to deduct the last type??
Thanks a lot.
Let's do this manually, step-by-step:
fun compCompA42 x = comp compA42 x
It's a function, so compCompA42 has type α -> β.
compCompA42's return value must be of the same type as comp compA42 x, i.e. β = typeof(comp compA42 x).
We already now the most general type for comp:
(a -> b) -> (b -> c) -> a -> c
Now, we need to specialize it for the case when a -> b = typeof(compA42) and (b -> c) = α:
a -> b = typeof(compA42) = (int -> d) -> int -> d. From this equation follows that a = int -> d and b = int -> d.
So, α = b -> c = (int -> d) -> c and β = typeof(comp compA42 x) = a -> c = (int -> d) -> c.
Finally, our most general type for compCompA42 is
α -> β = ((int -> d) -> c) -> (int -> d) -> c.
Observe that you can always make some SML interpreter (e.g., smlnj) show you types:
- fun compCompA42 x = comp compA42 x;
val compCompA42 = fn : ((int -> 'a) -> 'b) -> (int -> 'a) -> 'b
And it's the same type we've got manually (just rename d to 'a and c to 'b).
I'am trying to decrypt the return of calc function below but i'am very confused.
I have the f function that takes 3 ints and returns an int.
The calc fuction i think should return val calc : int -> int = <fun> because f has to take 3 ints, i'am giving it x and y so now it needs one more to return another int, the final result. Why it this logic not correct?
I can't make any sense of the actual output, specially with the polymorfic values when i forced parameters in f to be integers.
# let f (x : int) (y : int) (z : int) = x + y + z;;
val f : int -> int -> int -> int = <fun>
# let calc x y f = f x y;;
val calc : 'a -> 'b -> ('a -> 'b -> 'c) -> 'c = <fun>
In the expression let calc x y f = f x y;;, f is a locally-bound variable (calc binds x, y and then f) rather than the function you have defined before.
If you had written let calc x y = f x y;; then you'd have the expected result.
The function calc contains no reference to the functionf. There is an argument named f be it could be named g without changing anything: let calc x y g = g x y. If you want to use the function f you have defined above and not any function of the right type, you must not pass f as an argument, you should rewrite calc like this:
let f x y z = x + y + z
let calc x y = f x y
and then calc will have the type int -> int -> (int -> int) which is more commonly written as int -> int -> int -> int.
Are the two types: int -> int -> int and int -> (int -> int) the same?
If I write let f x = fun y -> x + y + 1, utop returns int -> int -> int. But I want is a function that takes an int as a parameter and then return a function which also takes an int and returns int, i.e., int -> (int -> int)
Is there a way to do that?
Also for (’a * ’b -> ’c) -> (’a -> ’b -> ’c), I wrote let f g = fun a b -> g (a,b), but it returns (’a * ’b -> ’c) -> ’a -> ’b -> ’c, the parentheses are eliminated. But why?
If a new function is returned as result, will it be anyway curried?
Usually, in lambda-calculus all functions have 1 arguments. Functions that take two arguments (not a tuple) are functions which take 1 argument and return another functions. If you look at the problem from this point of view you will understand that -> is right-associative.
Yes, they are the same. The arrow is a right-associative infix constructor, which is why the parentheses are redundant on the right.
It is perhaps helpful to realise that
let f x y z = e
is simply syntactic sugar for
let f = fun x -> fun y -> fun z -> e
Everything else follows form there.
Well yes, they are the same.
You can try it out, just type in
let f x y = x + y + 1;;
val f : int -> int -> int = <fun>
# let g = f 1;;
val g : int -> int = <fun>
# let _ = List.map g [1;2;3];; (* would do the same with (f 1) instead of g *)
- : int list = [3; 4; 5]
The whole idea is that function are by default curryfied. And as a -> ( b -> c ) and a -> b -> c are equivalent, they are displayed as the lightest type possible.