Types and function applications
Contents:
# function types
- type
t -> uis the type of a function which takes an input of typetand returns an output of typeu.
We can extend this to multiple arguments,
type
t1 -> t2 -> uis the type of a function which takes an input of typet1, another input of typet2and returns an output of typeu.So the type of a function is determined by the type of its arguments and the type of its output. This is why you cannot have different output types in ocaml for any branching conditions! That violates this rule.
# Partial function applications
When we define a multi-argument function, we don’t HAVE to pass in all the arguments at once. We can partially pass in arguments. For example:
x ^ y ^ z;;
(* val func : string -> string -> string -> string = <fun> *)
now I can say something like:
let a = func "Hi", " There!";;
This is perfectly valid! So what will this expression evaluate to? It’s going to assign a function to a, which is equivalent to a partially applied func on the arguments, that is:
(**Effectively:
val a : string -> string = <fun> *)
a " HelloWorld! ";; (*gives: "Hi There! HelloWorld! "*)
That’s awesome! But the reason it works is even more awesome! The truth is that OCaml actually doesn’t have multi-argument functions!!
A function like this:
let add = ;;
add 1 2 3;;
is completely equivalent to:
let add = ;;
add 1 2 3;;
These are all partial function applications! Damn. That’s awesome. This means multi-argument function functions are syntactical sugar for applying multiple single level anonymous functions. This actually comes directly from the founding father of functional programming Alonzo Church!
# Parametric polymorphic
These are function’s who don’t have a predefined type, that is, the compiler cannot figure out a single type to give such a function. This means that we can pass anything to such functions!
The simplest example is:
x;;
(* val f : 'a -> 'a = <fun> *)
Notice that ocaml assigns a special type to this, 'a (pronounced “alpha”). This is similar to “Any” in python (but nope, that’s just for humans and code editors, this is a compile time restriction!)
# Operators as functions
There are three types of ways of writing operations:
- Infix
- Postfix
- Prefix
We’re typically used to writing operations in the infix form, that is: 1+1=2, but prefix actually allows us to look at a subtle variation in functional programming:
1 2;; (*gives false*)
1 2;; (*gives : int = 3*)
2;; (*gives 0*)
4 5;; (*Gives 20, don't forget to put the space!*)
So, we can think of operators as functions as well if we look at the prefix form.
Let’s look at max:
max;;
(*- : 'a -> 'a -> 'a = <fun>*)
max 1 2;; (*gives 2*)
But we can define max as an infix operator as well
max x y;;
1 2;; (*This gives 2*)
1 <^> 2;; (*This also works and gives 2*)
# Function application operators
- The
application operatoris simple:
f x;; (*Takes in f and x, applies f to x*)
So, this lets us do something like:
print_endline @@ string_of_int 9;;
(*Which can also be written in the prefix format*)
print_endline
Both of which are an equivalent way of writing:
print_endline ;;
- The
reverse applicationis also simpler, just reverse of application:
f x;;
This takes in an argument and a function, and applies the function to the argument. How can we use it?
x * x;;
let a = 5 in
a |> succ |> square |> square;;
We basically say, increment 5, then square it (36), then square it again (1296). Without using the operator, we’d have to write:
x * x;;
let a = 5 in
square ;;
Which is ugly.