Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- (* ocaml's object system is quite unique and rarely used, despite it actually being pretty awesome
- * I call it "statically typed duck typing" because that's what it is
- *)
- (* let's define a """class"""
- * your basic example of a linked-list
- *)
- let node (x, y) = object
- method hd = x
- method tl = y
- method nth n =
- if n = 0 then
- x
- else
- y#nth (n - 1)
- method length =
- 1 + y#length
- end
- (* this isn't actually a class, this is a function called node that takes two arguments, and returns an object
- * objects can easily be created anonymously, in this case we simply create an anonynmous object inside the function body
- * this has methods and closes over the function arguments, this object is immutable
- * as such, there is really no "this/self" keyword used in this approach
- * this is your basic recursive implementation of a linked list's head, tail, indexing and length operations
- *)
- (*now, the type of this constructor is quite something and automatically inferred by the compiler and statically checked:
- *
- * node :
- * 'a * (< length : int; nth : int -> 'a; .. > as 'b) ->
- * < hd : 'a; length : int; nth : int -> 'a; tl : 'b >
- *
- * I've put it on two lines to make it clearer, the first is the input type. The second argument is the interesting one:
- * < length : int; nth : int -> 'a; .. > as 'b
- *
- * this reads an object with:
- * - a "length" method that returns an int
- * - a "nth" method that takes an int and returns something of the same type as the first argument to node.
- * - ANY FURTHER METHODS, this is what the dots indicate which is very important
- *
- * the quote before "a" means this is a type variable.
- * This means that both instances of 'a can be any type, as long as they are the same one
- *
- * then the output type is our object. Which has all our methods,
- * the most important one is that its "tl" method returns 'b, which we gave as a shorhand to the type of our second argument.
- * obviously we return the tail we gave as second argument in tact.
- *)
- (* we wil also make a nil object
- * this is not a function, OCaml just uses similar syntax to declare constants and functions
- * note that we give it hd and tl and nth methods, but raise exceptions on them.
- * This is done on purpose to give it the appropriate types.
- * After all. Our "node" constructor demanded that its second argument implement the "nth" and "length" methods.
- *)
- exception Empty_list
- let nil = object
- method hd = raise Empty_list
- method tl = raise Empty_list
- method nth n = raise Empty_list
- method length = 0
- end
- (* so now we can make some linked lists
- * probably not the most convenient syntax but you get the point
- * this will all type check, all the numbers are the same type, which is what the implementation demanded
- *)
- let int_list = node(0, node(1, node(2, node(3, nil))))
- let () = Printf.printf "%d\n" int_list#length (* 3 *)
- (* the constructors are polymorphic,
- * the elements of the list can be any type, as long as they are all the same type.
- *)
- let string_list = node("zero", node("one", node("two", node("three", nil))))
- let () = Printf.printf "%d\n" string_list#tl#length (* 2 *)
- (* but this would be a static typing error,
- *
- * let hetero_list = node(0, node("one", node(2.0, node(`Three, nil))))
- *)
- (* now, here it gets interesting.
- * Remember, all our implementation of the linked list wanted was
- * that the second argument implemented "nth" and "length" correctly.
- * so here's a fake parametic "structure" that is filled with factorials
- *)
- let weird_object = object
- val internal_array = [| 1,2,3,4 |]
- (* when we ask for the nth element, it in fact computes the factorial of said number *)
- method nth n =
- let rec factorial n =
- if n = 0 then
- 1
- else
- n * factorial (n - 1)
- in factorial n
- method length = 60
- end
- (* this array does not implement the hd and tl methods
- * but our linked list implementation didn't ask for that for its second elements
- * so, funnily enough, this works:
- *)
- let wtf_structure = node(0, node(4, weird_object))
- (* and so does this
- *)
- let () = Printf.printf "%d\n" wtf_structure#length (* 62 *)
- let () = Printf.printf "%d\n" (wtf_structure#nth 8) (* 720 *)
- (* but say we wanted to implement map on our custom list:
- *)
- let rec map_obj f obj =
- if obj#length = 0 then
- obj
- else
- node (f obj#hd, map_obj f obj#tl)
- (* so will this now work on our wtf_structure?
- * No! because the type of this function is inferred by the implementation that the second argument is:
- *
- * (< hd : 'a; length : int; nth : int -> 'a; tl : 'b > as 'b)
- *
- * the "as 'b" part is important.
- * It means that the "tl" method must in this case return an object which has all the methods listed there too.
- * and that's something our "weird object" doesn't provide.
- *)
- (* conclusion
- * OCaml's object system allows for "statically typed duck typing",.
- * meaning that the type of an object is just its methods and thier types
- * this system is surprisingly flexible. It statically checks if stuff quacks the right way.
- *
- * and yes, with explicit type annotations you can actually give a set of methods together a name
- * and there are even classes if you want them
- *)
Advertisement
Add Comment
Please, Sign In to add comment