Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of an arrow "->" in OCaml

Tags:

ocaml

I've been learning OCaml recently and as of now it would seem an arrow is used by the compiler to signify what the next type would be. For instance, int -> int -> <fun> an integer which returns an integer, which returns a function.

However, I was wondering if I can use it natively in OCaml code. In addition, if anyone would happen to know the appropriate name for it. Thank you.

like image 538
Charlie-Greenman Avatar asked Jan 04 '23 08:01

Charlie-Greenman


1 Answers

The operator is usually called type arrow where T1 -> T2 represents functions from type T1 to type T2. For instance, the type of + is int -> (int -> int) because it takes two integers and returns another one.

The way -> is defined, a function always takes one argument and returns only one element. A function with multiple parameters can be translated into a sequence of unary functions. We can interpret 1 + 2 as creating a +1 increment function (you can create it by evaluating (+) 1 in the OCaml command line) to the number 2. This technique is called Currying or Partial Evaluation.

Let's have a look at OCaml's output when evaluating a term :

# 1 + 2;;
- : int = 3

# (+) 1 ;;
- : int -> int = <fun>

The term1+2 is of type integer and has a value of 3 and the term (+) 1 is a function from integers to integers. But since the latter is a function, OCaml cannot print a single value. As a placeholder, it just prints <fun>, but the type is left of the =.

You can define your own functions with the fun keyword:

# (fun x -> x ^ "abc");;
- : bytes -> bytes = <fun>  

This is the function which appends "abc" to a given string x. Let's take the syntax apart: fun x -> term means that we define a function with argument x and this x can now appear within term. Sometimes we would like to give function names, then we use the let construction:

# let append_abc = (fun x -> x ^ "abc") ;;
val append_abc : bytes -> bytes = <fun> 

Because the let f = fun x -> ... is a bit cumbersome, you can also write:

let append_abc x = x ^ "abc" ;;
val append_abc : bytes -> bytes = <fun>

In any case, you can use your new function as follows:

# append_abc "now comes:" ;;
- : bytes = "now comes:abc"

The variable x is replaced by "now comes:" and we obtain the expression:

"now comes:" ^ "abc"

which evaluates to "now comes:abc".

@Charlie Parker asked about the arrow in type declarations. The statement

type 'a pp = Format.formatter -> 'a -> unit

introduces a synonym pp for the type Format.formatter -> 'a -> unit. The rule for the arrow there is the same as above: a function of type 'a pp takes a formatter, a value of arbitrary type 'a and returns () (the only value of unit type)

For example, the following function is of type Format.formatter -> int -> unit (the %d enforces the type variable 'a to become int):

utop # let pp_int fmt x = Format.fprintf fmt "abc %d" x;;
val pp_int : formatter -> int -> unit = <fun>

Unfortunately the toplevel does not infer int pp as a type so we don't immediately notice(*). We can still introduce a new variable with a type annotation that we can see what's going on:

utop # let x : int pp = pp_int ;;
val x : int pp = <fun>
utop # let y : string pp = pp_int ;;
Error: This expression has type formatter -> int -> unit
       but an expression was expected of type
         string pp = formatter -> string -> unit
       Type int is not compatible with type string 

The first declaration is fine because the type annotation agrees with the type inferred by OCaml. The second one is in conflict with the inferred type ('a' can not be both int and string at the same time).

Just a remark: type can also be used with records and algebraic data types to create new types (instead of synonyms). But the type arrow keeps its meaning as a function.

(*) Imagine having multiple synonymes, which one should the toplevel show? Therefore synonyms are usually expanded.

like image 194
lambda.xy.x Avatar answered Jan 21 '23 05:01

lambda.xy.x