Basic datastructures and their syntax and semantics
Contents:
# lists
We can define a list in the following way:
let a = [;;
(* val a : 'a list = []*)
Note the alpha there? that’s because we haven’t passed anything inside the list yet. That alpha will take the type of the value present inside the list.
To define a more useful list, we separate the elements out using a “;”.
let a = ;;
(* val a : int list = [1;2;3]*)
Now, here’s one thing you cannot do: Add elements to the list with different types! So, ["abcd"; 'a'] is not allowed (yet).
We will see later on how to have lists with different types cause that’s actually a really important thing to have in any real usecase!
We can have nested lists:
let a = ;;
(*val a : int list list = [[1;2];[1;4];[3;2]]*)
Now you can add an element to a list using the :: operator.
0::;; (*gives [0;1;2;3]*)
- OCaml lists are always immuatable, once something goes inside a list, you can’t change it
- You will have to redefine a new list if you want to make any changes
- Lists are “singly-linked” => They are ordered, and they work okayish only.
# List syntax and semantics
Syntax:
[]=> Is an empty liste1 :: e2=> prepends elemente1to liste2[e1;e2]is syntactical sugar fore1::e2::[]
The :: operator is called “cons” which comes from LISP.
Semantics:
- To evaluate a list like:
[e1;e2], we first evaluatee1tov1ande2tov2 - We return
[v1;v2]
# Records and tuples
Records are another basic datatype built into OCaml. To define that we use:
This is a record type and we can create values of it, for example:
;;
(*val me : student = {name = "Something"; grad_year = 20120}*)
Now I can access individual elements of this record:
print_endline @@ string_of_int me.grad_year;;
print_endline me.name;;
Its essentially a dictionary acess if you think about it. But its also a little different.
# Tuples
Tuples are for created grouped data. You can create a type for your tuple as well!
;;
We can also define it for points in a cartesian plane for example:
;;
The real magic for the lists and tuples and records and all, come when we do matching! Which is coming soon. I belive.
Also you can unpack the tuple to access individual values:
let = p1;;
a;; (*gives 1.2*)
OCaml also has an inbuilt function called fst which is the kestrel operator from lambda calculus. It takes in two arguments and returns the first one!
fst;;
(** 'a * 'b -> 'a = <fun> *)
let a = fst p1;;
(* a becomes 1.2 *)
We can get the second component using snd which is like kestrel but reversed. Gives the second component after taking in two. You can clearly see that fst and snd only work when you have two elements in the tuple. For others, we unpack.
# Deciding datatypes
The question now is, how do you choose between lists, tuples and records? Here’s the rule of thumb (not necessary but usually works):
- If you have unbounded data:
lists - If you have bounded data but you want to access it by position:
tuples - if you have bounded data and you want to access it by name:
records
Tuples are bounded because while we have ways to “extend” a list due to its “linked” nature. Both lists and tuples are immutable, and extending a list does give us a new list. But the idea is that every extension plays neatly into how the data is arranged.
# Record syntax and semantics
One cool thing about field names in the type definition is that, we can have upto 4 million of them (I am not sure what’s so special about 4 million).
e.flets you access fieldfof a record expressione. Thefis an identifier, not an expression to be computed.
Type checking: If e:t and if t is defined as { t1 : v1 ... ti : vi ... }, then e.ti = vi. Which basially means, e is a record of type t.
- Records and types are both immutable. For example, consider:
;;
(*val square : rectangle = {height = 1; width = 1}*)
;;
(*- : rectangle = {height = 2; width = 1}*)
square;;
(*- : rectangle = {height = 1; width = 1}*)
In this case, we have NOT mutated the value of square. We have created a copy of the record square with a field changed. You cannot change fields once they are set! This is because the with is also syntactical sugar for defining a new record.
# Tuple syntax and semantics
- If
ei ==> vithen(e1, ... , en) ==> (vi, ... , vn). - If
ei : tithen(e1, ... , en) : t1 * ... * tn.
Pretty simple this one right. Note that the order is very much relevant here!