Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 [Tutorial] From Turing to O'Caml - Days 1 through 14
Index -> Programming, Turing -> Turing Tutorials
Goto page 1, 2  Next
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: Tue Jul 01, 2008 10:25 pm   Post subject: [Tutorial] From Turing to O'Caml - Days 1 through 14

Day 1

Get the O'Caml tools!

The toplevel

Run the "ocaml" command. You will see the following in a command window.

code:
        Objective Caml version 3.10.0

#


Exit it with Ctrl+z or Ctrl+d on Linux.

This will be your primary tool for learning O'Caml. More on that tomorrow.

Day 2

Values

Common types of data to work with in Turing are integers, floating point (or "real") numbers, strings, characters and booleans. In O'Caml they're essentially the same. We can see these in the toplevel easily.

code:
        Objective Caml version 3.10.0

# 1;;
- : int = 1
# 42;;
- : int = 42
# 3.14159;;
- : float = 3.14159
# "Hello";;
- : string = "Hello"
# 'a';;
- : char = 'a'
# true;;
- : bool = true
# false;;
- : bool = false
#


The double semi-colon ends an expression in the O'Caml toplevel. After each line the interpreter shows us the value, along with the type and any name bound to that value. Here "-" indicates the value is bound to no name.

Some simple operations

code:
# 1 + 2 * 3;;
- : int = 7


Note that the order of operations you are used to is maintained.

Operators are not overloaded, as you will see below.

code:
# 4.2 +. 2. *. 3.712;;     
- : float = 11.624
# "Hello" ^ " " ^ "world";;
- : string = "Hello world"
# (true || false) && false;;
- : bool = false
#


Day 3

Giving names to things

In Turing, you'd write something like the following.

code:
var foo : int := 42


In O'Caml, we'd do much the same with the following (in the toplevel).

code:
# let foo = 42;;
val foo : int = 42
#


The feedback from the toplevel has changed now. It now tells us that what it sees is a value named "foo".

We can go on to use that name in expressions.

code:
# foo + 27;;
- : int = 69
# 84 / foo + 6;;
- : int = 8
# let bar = foo * 2;;
val bar : int = 84
#


Now, experiment with other names and other values.

Day 4

Functions

Let's start simple.

code:
function foo(bar : int) : int
    result bar * 3
end foo


Here the types involved in the function "foo" are explicitly delimited.

code:
# let foo bar = bar * 3;;
val foo : int -> int = <fun>
#


The types involved in this O'Caml function are not explicitly stated at all. They are instead inferred based on the usage. Since operators are not overloaded, the only type "bar" could possibly have is "int" and the type the function must return is also "int".

The "int -> int" notation for the type of the value "foo" indicates that it is a function which takes one int and returns an int. This the language can figure out without us needing to tell it.

We can see the type of a function by just entering its name.

code:
# foo;;
- : int -> int = <fun>
#


A few existing functions

Let's say we have a floating point number we want to pass to our function foo. We'd need to explicitly change it. The "int_of_float" function would come in handy.

code:
# foo 3;;     
- : int = 9
# int_of_float 4.5;; 
- : int = 4
# foo (int_of_float 4.5);;
- : int = 12
# let bar = 6.3;;
val bar : float = 6.3
# foo (int_of_float bar);;
- : int = 18
#


And let's print something.

code:
# string_of_int;;
- : int -> string = <fun>
# string_of_int 56;;
- : string = "56"
# print_endline "hello";;
hello
- : unit = ()
# string_of_int (foo (int_of_float bar));;
- : string = "18"
# print_endline (string_of_int (foo (int_of_float bar)));;
18
- : unit = ()
#


Day 5

"Variables" revisited

Variables in O'Caml aren't variable. We are simply binding a name to a value. What we have done so far would be more akin to creating constants in Turing. This is a good thing.

Local variables

Consider:

code:
print_endline (string_of_int (foo (int_of_float bar)))


This expression may seem unwieldy. Let's use local bindings to clean it up.

code:
# let str = string_of_int (foo (int_of_float bar)) in
      print_endline str;;
18
- : unit = ()
# let i = int_of_float bar in
  let s = string_of_int (foo i) in
      print_endline s;;
18
- : unit = ()
#


Of course, the types involved in local binding are inferred.

Exercise

Write a function which takes an integer argument, converts it to a floating point number, and then prints that number. Use local bindings to store the floating point number and string before using them.

Day 6

Records

Let's define a simple record and a variable holding a record of that type, then we'll print out that information.

code:
type Student :
    record
        name : string
        grade : int
    end record

var bob : Student

bob.name := "Bob Smith"
bob.grade := 75

put bob.name, " has a grade of ", bob.grade


And now in O'Caml.

code:
# type student = { name : string; grade : int };;
type student = { name : string; grade : int; }
#  let bob = { name="Bob Smith"; grade=75 };;
val bob : student = {name = "Bob Smith"; grade = 75}
# print_string (bob.name ^ " has a grade of ");
  print_int bob.grade;
  print_newline ();;
Bob Smith has a grade of 75
- : unit = ()
#


Functions with multiple expresions

Let's organize that bit of code.

code:
type Student :
    record
        name : string
        grade : int
    end record

var bob : Student

bob.name := "Bob Smith"
bob.grade := 75

procedure student_report (s : student)
    put s.name, " has a grade of ", s.grade
end student_report

student_report (bob)


code:
# type student = { name : string; grade : int };;
type student = { name : string; grade : int; }
#  let bob = { name="Bob Smith"; grade=75 };;
val bob : student = {name = "Bob Smith"; grade = 75}
# let student_report s =
      print_string (s.name ^ " has a grade of ");
      print_int s.grade;
      print_newline ();;
val student_report : student -> unit = <fun>
# student_report bob;;
Bob Smith has a grade of 75
- : unit = ()
#


Note that just based on the fields we used on "s" allowed O'Caml to infer the type of that argument. But, O'Caml has another trick up its sleeve.

Pattern matching

code:
# let student_report { name=n; grade=g } =
      print_string (n ^ " has a grade of ");
      print_int g;
      print_newline ();;
val student_report : student -> unit = <fun>
# student_report bob;;
Bob Smith has a grade of 75
- : unit = ()
#


Day 7

Conditionals

code:
type Student :
    record
        name : string
        grade : int
    end record

var bob : Student

bob.name := "Bob Smith"
bob.grade := 75

procedure student_report (s : student)
    if s.grade >= 65 then
        put s.name, " passed with a grade of ", s.grade
    else
        put s.name, " failed with a grade of ", s.grade
    end if
end student_report

student_report (bob)


code:
# type student = { name : string; grade : int };;
type student = { name : string; grade : int; }
#  let bob = { name="Bob Smith"; grade=75 };;
val bob : student = {name = "Bob Smith"; grade = 75}
# let student_report s =
      if s.grade >= 65 then
          print_string (s.name ^ " passed with a grade of ")
      else
          print_string (s.name ^ " failed with a grade of ");
      print_int s.grade;
      print_newline ();;
val student_report : student -> unit = <fun>
# student_report bob;;
Bob Smith passed with a grade of 75
- : unit = ()
#


A different take

code:
type Student :
    record
        name : string
        grade : int
    end record

var bob : Student

bob.name := "Bob Smith"
bob.grade := 75

procedure student_report (s : student)
    var msg : string

    if s.grade >= 65 then
        msg := " passed with a grade of "
    else
        msg := " failed with a grade of "
    end if

    put s.name, msg, s.grade
end student_report

student_report (bob)


code:
# type student = { name : string; grade : int };;
type student = { name : string; grade : int; }
#  let bob = { name="Bob Smith"; grade=75 };;
val bob : student = {name = "Bob Smith"; grade = 75}
# let student_report {name=n; grade=g} =
      let msg =
          if g >= 65 then
              " passed with a grade of "
          else
              " failed with a grade of "
      in
          print_string (n ^ msg);
          print_int g;
          print_newline ();;
val student_report : student -> unit = <fun>
#


What's going on here?

A conditional in Turing can only have side-effects. It can only do something, like assigning a new value to a variable.

On the other hand, an O'Caml conditional evaluates to a value. In this case, that greatly simplifies binding a value to "msg". We can even distill this down further.

code:
# type student = { name : string; grade : int };;
type student = { name : string; grade : int; }
# let bob = { name="Bob Smith"; grade=75 };;
val bob : student = {name = "Bob Smith"; grade = 75}
# let student_report {name=n; grade=g} =
      let msg =
          (if g >= 65 then "passed" else " failed") ^ " with a grade of"
      in
          print_string (n ^ " " ^ msg ^ " ");
          print_int g;
          print_newline ();;
val student_report : student -> unit = <fun>
# student_report bob;;
Bob Smith passed with a grade of 75
- : unit = ()
#


Functions can be constructed with pattern matching in another way. Guard patterns let us limit a pattern. The expression associated with the first pattern to match the input will be evaluated.

code:
# type student = { name : string; grade : int };;
type student = { name : string; grade : int; }
# let bob = { name="Bob Smith"; grade=75 };;
val bob : student = {name = "Bob Smith"; grade = 75}
# let student_report = function
      {name=n; grade=g} when g >= 65 ->
          print_string (n ^ " passed with a grade of ");
          print_int g;
          print_newline ()
    | {name=n; grade=g} ->
          print_string (n ^ " failed with a grade of ");
          print_int g;
          print_newline ();;
val student_report : student -> unit = <fun>
# student_report { grade=42; name="Brian" };;
Brian failed with a grade of 42
- : unit = ()
#


Day 8

Writing a program

So far we've evaluated expressions in the interactive environment, but it's about time to move on to writing a program in its entirety.

So, open your favorite text editor, and create a file called "helloworld.ml" with the following code.

code:
print_endline "Hello, world!"


Now, open a terminal window and navigate to the directory where you saved your source file. If you have questions on doing this in Windows, please ask.

We'll now compile it using the following:

code:
ocamlc helloworld.ml -o helloworld


If you're using Windows, you should have it tack a ".exe" onto your executable name.

code:
ocamlc helloworld.ml -o helloworld.exe


That executable can then be run the same way any other executable would be.

Of course, that's exceptionally trivial, but it's a start. Let's incorporate something else.

code:
print_string "Please enter your name: ";;

let name = read_line ();;

print_endline ("Hello, " ^ name ^ "!")


Let's add a few functions in.

code:
let greet_nicely name =
    print_endline ("Hello, " ^ name ^ "!");;

let greet_rudely name =
    print_endline ("Bugger off " ^ name ^ "...");;

let greet = greet_nicely;;

print_string "Please enter your name: ";;

let name = read_line ();;

greet name;;
greet_nicely name;;
greet_rudely name


Day 9

Modules

Wouldn't it be nice if we could put all of those related functions in a module, to separate them out from other code?

code:
module Greetings =
    struct
        let greet_nicely name =
            print_endline ("Hello, " ^ name ^ "!");;

        let greet_rudely name =
            print_endline ("Bugger off " ^ name ^ "...");;

        let greet = greet_nicely
    end;;

print_string "Please enter your name: ";;

let name = read_line ();;

Greetings.greet name;;
Greetings.greet_nicely name;;
Greetings.greet_rudely name


But, now why don't we put it in a separate file, so any program can use it?

Well, create a new file called "greetings.ml". The name of the file should match the name of the module it will contain, but begin with a lowercase letter. In it we'll put the following code.

code:
module Greetings =
    struct
        let greet_nicely name =
            print_endline ("Hello, " ^ name ^ "!");;

        let greet_rudely name =
            print_endline ("Bugger off " ^ name ^ "...");;

        let greet = greet_nicely
    end


Now, our main program:

code:
open Greetings;;

print_string "Please enter your name: ";;

let name = read_line ();;

Greetings.greet name;;
Greetings.greet_nicely name;;
Greetings.greet_rudely name


The "open ..." expression instructs O'Caml to make a module available to us for use.

And to compile, we must instruct the compiler to compile the module as well.

code:
ocamlc greetings.ml helloworld.ml -o helloworld


And one last module trick for today. Let's say that appending "Greetings" to everything gets tedious. We could "include Greetings" which would bring all of Greetings into the scope of our program, or...

code:
module G = Greetings


Or we can even handle this locally.

code:
let module G = Greetings in
    G.greet name;
    G.greet_nicely name;
    G.greet_rudely name;;


Day 10

Let's create a linked list

Untested

code:
type Node :
    record
        next : ^Node
        has_next : boolean
        data : int
    end record

function new_node (data : int) : ^Node
    var n : ^Node
   
    new Node, n
    n -> data := data
    n -> has_next := false

    result n
end new_node

procedure add_to_list (root : ^Node, data : int)
    if root -> has_next then   
        add_to_list (root -> next, data)
    else
        root -> has_next := true
        root -> next := new_node (data)
    end if
end add_to_list


The above uses pointer semantics because pointers do not need to have a value assigned with them, so the recursive nature of the type doesn't cause Turing to fill the whole of the known universe with copies. The boolean field allows me to know when there is a next value.

Now, let's take the tiniest of steps in O'Caml and define the type...

Variant types

code:
# type node = Node of int * node | Empty;;
type node = Node of int * node | Empty
#


We now have a type "node" with two constructors. One of those takes a single argument composed of a tuple of an integer and another node, and the other is a "unary constructor". It takes no arguments.

Let's create a basic node.

code:
# Node (42, Empty);;
- : node = Node (42, Empty)
#


We need a function to determine if the node has a next value. We'll use pattern matching.

code:
# let has_next = function
      Empty | Node (_, Empty) -> false
    | _ -> true;;
val has_next : node -> bool = <fun>
#


Here the underscore is saying, "I don't really care what ata is stored in the node, so don't bother giving it a name."

In general it's a function which takes one argument and matches that against three possibilities. Either the node will be entirely Empty, the node will have data, but the next node will be Empty, or it will have data and have a next node. That last case is obvious when the others are ruled out, so it needn't be named.

Recursion

I used recursion in the Turing example to add a new node to the list. The diference in O'Caml will be that I cannot mutate the existing list, and must instead create a new one. O'Caml is exceptionally efficient, so this is not a performance issue.

code:
# let rec add_to_list root_node new_data =
      match root_node with
          Empty ->
              Node (new_data, Empty)
        | Node (current_data, Empty) ->
              Node (current_data, Node (new_data, Empty))
        | Node (current_data, next_node) ->
              Node (current_data, add_to_list next_node new_data);;
val add_to_list : node -> int -> node = <fun>
#


The "rec" signifies that this will be a recursive function.

Again, we handle three situations. This time each has a unique result.

For adding data to an empty list, we simply get a node with a single value. Adding a value to a list with a value, but no next node is handled by creating a new list with the data in the current list, plus a next node with the new data.

The recursion happens in the event of the last case, where the next node is something other than Empty. In this event we create a new node with the current data, and the new next node is the result of applying the function to the current next node.

Perhaps it's easiest to see it worked out for an example.

code:
add_to_list (Node (42, Node (56, Node (73, Empty)))) 34
Node (42, add_to_list (Node (56, Node (73, Empty))) 34)
Node (42, Node (56, add_to_list (Node (73, Empty)) 34))
Node (42, Node (56, Node (73, Node (34, Empty))))


Sure enough, if I try this in the interpreter:

code:
# add_to_list (Node (42, Node (56, Node (73, Empty)))) 34;;
- : node = Node (42, Node (56, Node (73, Node (34, Empty))))
#


Of course, that function only really needs one terminating condition.

code:
# let rec add_to_list root_node new_data =
      match root_node with
          Empty ->
              Node (new_data, Empty)
        | Node (current_data, next_node) ->
              Node (current_data, add_to_list next_node new_data);;
val add_to_list : node -> int -> node = <fun>
#


Generics

Now, one last trick for the day. In Turing I need to recode the list type for every type of data I want to store in such a thing. I could do that in O'Caml, but why not just have the node type use a generic type?

code:
# type 'a node = Node of 'a * 'a node | Empty;;
type 'a node = Node of 'a * 'a node | Empty
# Node (4.5, Empty);;
- : float node = Node (4.5, Empty)
# Node ("Hello", Empty);;
- : string node = Node ("Hello", Empty)
#


The functions we defined earlier can be used without any changes save their inferred types.

Day 11

A more generic recursive function

There are any number of functions we could write to work on our linked list type. Most will be recursive. Let's look at finding the length of a list.

code:
# let rec length =
      function
          Empty -> 0
        | Node (_, next_node) -> 1 + length next_node;;
val length : 'a node -> int = <fun>
# length (Node (42, Node (73, Empty)));;
- : int = 2
#


Now, let's rewrite this function to use an accumulator.

code:
# let rec length acc n =
      match n with
          Empty -> acc
        | Node (_, next_node) -> length (acc + 1) next_node;;
val length : int -> 'a node -> int = <fun>
#


But why do this? Well, now each recursive call of the function can go on entirely without sending any information back to the calling function. This permits the compiler to heavily optimize the function.

Let's take a closer look at what "length" actually does. It loops over a list. If the list is empty it simply returns some set value it is given. If the list is not empty it applies some function to the value it's given and the current value in the list, though that value is ignored. It uses the result of this function application as the next value for the call to length and passes the remainder of the list as the list to work on in the next call.

All we need to do to abstract this further is to be able to specify which function is applied.

code:
# let rec fold_list fnc initial_value lst =
      match lst with
          Empty -> initial_value
        | Node (value, next_node) ->
              fold_list fnc (fnc initial_value value) next_node;;
val fold_list : ('a -> 'b -> 'a) -> 'a -> 'b node -> 'a = <fun>
#


Whoa! What's that inferred type signature mean?

code:
('a -> 'b -> 'a) -> 'a -> 'b node -> 'a


Two types are involved, 'a and 'b. These are generic types. They can be anything.

The fold_list function takes three arguments:

code:
('a -> 'b -> 'a)


code:
'a


and:

code:
'b node


That first argument has more arrows. Don't be too confused. That argument is itself a function. Yes, functions can be passed as arguments to other functions. This function is one which takes two arguments of types 'a and 'b and returns type 'a.

The second two arguments are easy. The return value is fairly easy as well.

Maybe it's easier to understand if we actually use it.

code:
# let add_int_to_float x y =
      x +. float_of_int y;;
val add_int_to_float : float -> int -> float = <fun>
#


If we consider this in terms of the first argument to our fold_list function, 'a would be float, and 'b would be int.

Obviously our 'b node will be an int node and the initial value passed to fold_list will be a float value.

code:
# fold_list add_int_to_float 0. (Node (4, Node (78, Empty)));;
- : float = 82.
#


A convenience

Having to name a function for this purpose is inconvenient.

code:
# fold_list (fun a b -> a +. float_of_int b) 0. (Node (4, Node (78, Empty)));;
- : float = 82.
#


A few uses

One sum function:

code:
# let rec sum n =
      match n with
          Empty -> 0
        | Node (v, next_node) -> v + sum next_node;;
val sum : int node -> int = <fun>
# sum (Node (4, Node (5, Empty)));;
- : int = 9
#


Or...

code:
# let sum n = fold_list (fun a b -> a + b) 0 n;;
val sum : int node -> int = <fun>
# sum (Node (4, Node (5, Empty)));;
- : int = 9
# let sum n = fold_list (+) 0 n;;
val sum : int node -> int = <fun>
# sum (Node (4, Node (5, Empty)));;
- : int = 9
#


A function to determine if a list contains a given value.

code:
# let has_value v lst =
      fold_list (fun a b -> if b = v then true else a) false lst;;
val has_value : 'a -> 'a node -> bool = <fun>
# has_value 45 (Node (42, Node (56, Node (45, Node (72, Empty)))));;
- : bool = true
# has_value 45 (Node (42, Node (56, Node (47, Node (72, Empty)))));;
- : bool = false
#


What if we want a nice way to print a list?

code:
# let string_of_node n str_func =
      "[" ^ fold_list (fun a b -> let s = str_func b in
                                      if a = "" then s
                                      else a ^ ", " ^ s)
                      "" n ^ "]";;
val string_of_node : 'a node -> ('a -> string) -> string = <fun>
# string_of_node (Node (5, Node (6, Empty))) string_of_int;;
- : string = "[5, 6]"
# string_of_node (Node (5., Node (6., Node (7., Empty)))) string_of_float;;
- : string = "[5., 6., 7.]"
#


In summary

Were this Turing, if we wanted to write a length function, or a function to stringify a list, or a has_value function... those functions would all have to repeat the boilerplate of the looping. In O'Caml through the use of functions that can be passed around and anonymously created we can easily abstract away the tedious pieces of logic.

I'm sure this has been quite a mind trip. Take time to let it sink in.

Day 12

Real variables

I mentioned early in this tutorial that O'Caml bindings are just the binding of a value to a name. They cannot be changed.

This is true. However, it is possible to create variables in the Turing sense.

code:
# let foo = ref 42;;
val foo : int ref = {contents = 42}
# foo.contents;;
- : int = 42
# !foo;;
- : int = 42
# foo := 65;;
- : unit = ()
# !foo;;
- : int = 65
# foo.contents <- 76;;
- : unit = ()
# !foo;;
- : int = 76
#


Now, I'm also going to introduce O'Caml's lists. They're conceptually the same as the one I showed you, but the syntax is a bit nicer. Oh, and a few types of procedural loops too.

code:
# let numbers = [1; 2; 3]
  and sum = ref 0
  in
      for i = 0 to List.length numbers - 1 do
          sum := !sum + List.nth numbers i
      done;
      !sum;;
- : int = 6
# let numbers = [6; 5; 19]
  and sum = ref 0
  and counter = ref 0
  in
      while !counter < List.length numbers do
          sum := !sum + List.nth numbers !counter;
          counter := !counter + 1
      done;
      !sum;;
- : int = 30
#


Day 13

Object-oriented programming

Let's take a code-heavy approach. A very simple class.

Turing:

code:
class Foo
end Foo


O'Caml:

code:
# class foo =
      object
      end;;
class foo : object  end
#


Let's create a new object.

Turing:

code:
var bar : pointer to Foo

new Foo, bar


O'Caml:

code:
# let bar = new foo;;
val bar : foo = <obj>
#


Let's add a method.

code:
class Foo
    export name

    function name : string
        result "foo"
    end name
end Foo

var bar : pointer to Foo

new Foo, bar

var name : string := bar -> name

put name


o'Caml:

code:
# class foo =
      object
          method name = "foo"
      end;;
class foo : object method name : string end
# let bar = new foo in
      print_endline bar#name;;
foo
- : unit = ()
#


Note that the O'Caml method is statically-typed, but that the type has been inferred.

Now, let's put some information into the class.

Turing:

code:
class Foo
    export initialize, get_name

    var name : string

    procedure initialize (name_ : string)
        name := name_
    end initialize

    function get_name : string
        result name
    end get_name
end Foo

var bar : Foo

new Foo, bar

bar -> initialize ("baz")

var name : string := bar -> get_name

puts name


This is pretty straightforward. Our class now contains a variable. Via the initialize procedure we can give that

variable a value. We can get that value with the get_name function. We cannot adirectly change "name" because it

is not exported.

Let's see it in O'Caml:

code:
# class foo name =
      object
          method get_name : string = name
      end;;
class foo : string -> object method get_name : string end
# let bar = new foo "baz" in
  let name = bar#get_name in
      print_endline name;;
baz
- : unit = ()
#


In the O'Caml code there is no variable. We simply construct the class using an argument "name" which get_name

returns. We could do something classer to the Turing example.

code:
# class foo name =
      object
          val mutable name_ = name
          method get_name : string = name_
      end;;
class foo :
  string -> object val mutable name_ : string method get_name : string end
# let bar = new foo "baz" in
  let name = bar#get_name in
      print_endline name;;
baz
- : unit = ()
#


But that's really quite pointless unless we alter the value of name at some point.

code:
# class foo name =
      object
          val mutable name_ = name
          method get_name : string = name_
          method set_name new_name = name_ <- new_name
      end;;
class foo :
  string ->
  object
    val mutable name_ : string
    method get_name : string
    method set_name : string -> unit
  end
# let bar = new foo "baz" in
  let original_name = bar#get_name in
      bar#set_name "wooble";
      let new_name = bar#get_name in
          print_endline original_name;
          print_endline new_name;;
baz
wooble
- : unit = ()
#


Interfaces

Now, let's magine a function which takes a Foo object as an argument.

code:
function blah (f : pointer to Foo) : string
    result f -> get_name
end blah


Pretty straightforward. But, what if I want blah to accept any object that responds to the get_name method?

Well, then I need that class to inherit from a class with a deferred function get_name.

code:
class HasName
    export get_name

    deferred function get_name : string
end HasName

class Foo
    inherit HasName

    body function get_name : string
        result "bar"

    end get_name
end Foo

function blah (f : pointer to HasName) : string
    result f -> get_name
end blah

var a : pointer to HasName

new Foo, a

put blah (a)


O'Caml takes a bit of a different approach. We've seen a lot of type inferencing so far. Let's see it again.

code:
# class foo name =
      object
          val mutable name_ = name
          method get_name : string = name_
          method set_name new_name = name_ <- new_name
      end;;
class foo :
  string ->
  object
    val mutable name_ : string
    method get_name : string
    method set_name : string -> unit
  end
# let blah f : string =
      f#get_name;;
val blah : < get_name : string; .. > -> string = <fun>
#


Here the type of the object passed in as an argument is inferred to be any object with at least a method get_name

that returns string. The ".." indicates it may also contain other methods. Only if the object has this method can

the function work, so the compiler figures out that that is what the type must be.

Of course, we have to tell the function that it returns a string, because the results of get_name are never used in

any way which indicates it must be a string.

Day 14

Recap

It's time for a breather. A chance to look back at what's been touched on thus far.

Day 1

Getting O'Caml and starting the interactive toplevel environment.

Day 2

Values, toplevel feedback and simple operations.

Day 3

Binding names to values.

Day 4

Defining simple functions and a glimpse of a few we can use from the library.

Day 5

Local name bindings.

Day 6

Records, functions with multiple expressions and a bit of pattern matching.

Day 7

Conditionals - we saw that they can return values as well as perform actions.

Day 8

Writing a program, rather than evaluating single expressions in the toplevel.

Day 9

Modules both inline in a source file and as a separate file.

Day 10

Variant types, recursion and generics.

Day 11

Getting more generic with recursion using a folding technique. This involved passing a function to another function as an argument. To accomodate this we saw the introduction of anonymous functions.

Day 12

Mutable values, O'Caml's own list type and procedural loops.

Day 13

Object-oriented programming - basic classes, interfaces and type inferencing.
Sponsor
Sponsor
Sponsor
sponsor
Saad




PostPosted: Sun Jul 06, 2008 6:04 pm   Post subject: Re: [Tutorial] From Turing to O'Caml - Days 1 through 7

Tutorial looks good, and is great for anyone looking for a new language after Turing. I can see wtd but great time and effort into this. I'll give some suggestions on IRC but other then that great job Smile.
rizzix




PostPosted: Mon Jul 07, 2008 1:26 am   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 4

Nice work wtd!
michaelp




PostPosted: Mon Jul 07, 2008 3:52 pm   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 4

Days 1 through 7 now... great work wtd!
wtd




PostPosted: Sat Jul 12, 2008 3:11 pm   Post subject: Re: [Tutorial] From Turing to O'Caml - Days 1 through 8

Bump for edits.
michaelp




PostPosted: Sat Jul 12, 2008 3:31 pm   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 9

Razz
Day 8 is repeated.
rdrake




PostPosted: Sat Jul 12, 2008 3:57 pm   Post subject: Re: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 9

michaelp @ Sat Jul 12, 2008 3:31 pm wrote:
Razz
Day 8 is repeated.
Little typo, fixed now. Any O'Caml-related questions?
michaelp




PostPosted: Sat Jul 12, 2008 4:07 pm   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 9

No, I'm good. (for now!)
I have a cast on (full arm) so I can't really do much programming ATM.
Sponsor
Sponsor
Sponsor
sponsor
wtd




PostPosted: Sat Jul 12, 2008 6:58 pm   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 9

But I did it just for you! With all of the type-inferencing, there's less to type.
Saad




PostPosted: Sat Jul 12, 2008 7:17 pm   Post subject: Re: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 9

wtd @ Sat Jul 12, 2008 6:58 pm wrote:
But I did it just for you! With all of the type-inferencing, there's less to type.


Offtopic but, I couldn't help but laugh out loud reading that. Nicely said Laughing
wtd




PostPosted: Thu Jul 17, 2008 4:30 am   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 11

Day 11 has been added. It is quite a bit to digest for the uninitiated. Take it slow and ask questions. Smile
Zampano




PostPosted: Thu Jul 17, 2008 6:06 am   Post subject: Re: [Tutorial] From Turing to O'Caml - Days 1 through 11

That's very thorough.
michaelp




PostPosted: Thu Jul 17, 2008 9:01 am   Post subject: Re: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 11

wtd @ Thu Jul 17, 2008 4:30 am wrote:
Day 11 has been added. It is quite a bit to digest for the uninitiated. Take it slow and ask questions. Smile

Aw, for me? You shouldn't have! Rolling Eyes
wtd




PostPosted: Sat Jul 19, 2008 5:08 pm   Post subject: RE:[Tutorial] From Turing to O\'Caml - Days 1 through 11

Uninitiated when it comes to functional programming.
wtd




PostPosted: Sat Jul 26, 2008 12:45 am   Post subject: Re: [Tutorial] From Turing to O'Caml - Days 1 through 14

The recap for days 1 through 13 has been posted. So, any questions?
Display posts from previous:   
   Index -> Programming, Turing -> Turing Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 2  [ 18 Posts ]
Goto page 1, 2  Next
Jump to:   


Style:  
Search: