Computer Science Canada Programming C, C++, Java, PHP, Ruby, Turing, VB   Username:   Password: Wiki   Blog   Search   Turing   Chat Room  Members
[Tutorial] From Turing to O'Caml - Days 1 through 14
Author Message
wtd

Posted: 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 = #

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 = #

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 = # 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 = # 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 = # 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 = # 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 = #

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 = # 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 = # 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 = #

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 = #

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 = #

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 = # 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 = #

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 = #

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 = #

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 = # 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 = # sum (Node (4, Node (5, Empty)));; - : int = 9 # let sum n = fold_list (+) 0 n;; val sum : int node -> int = # 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 = # 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 = # 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 = #

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 = #

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.

Posted: 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 .
rizzix

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

Nice work wtd!
michaelp

Posted: 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

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

Bump for edits.
michaelp

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

Day 8 is repeated.
rdrake

Posted: 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:

Day 8 is repeated.
Little typo, fixed now. Any O'Caml-related questions?
michaelp

Posted: 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.

wtd

Posted: 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.

Posted: 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
wtd

Posted: 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.
Zampano

Posted: 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

Posted: 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.

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

Posted: 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

Posted: 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: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First

Page 1 of 2  [ 18 Posts ]
Goto page 1, 2  Next
 Jump to:  Select a forum  CompSci.ca ------------ - Network News - General Discussion     General Forums   -----------------   - Hello World   - Featured Poll   - Contests     Contest Forums   -----------------   - DWITE   - [FP] Contest 2006/2008   - [FP] 2005/2006 Archive   - [FP] 2004/2005 Archive   - Off Topic     Lounges   ---------   - User Lounge   - VIP Lounge     Programming -------------- - General Programming     General Programming Forums   --------------------------------   - Functional Programming   - Logical Programming   - C     C   --   - C Help   - C Tutorials   - C Submissions   - C++     C++   ----   - C++ Help   - C++ Tutorials   - C++ Submissions   - Java     Java   -----   - Java Help   - Java Tutorials   - Java Submissions   - Ruby     Ruby   -----   - Ruby Help   - Ruby Tutorials   - Ruby Submissions   - Turing     Turing   --------   - Turing Help   - Turing Tutorials   - Turing Submissions   - PHP     PHP   ----   - PHP Help   - PHP Tutorials   - PHP Submissions   - Python     Python   --------   - Python Help   - Python Tutorials   - Python Submissions   - Visual Basic and Other Basics     VB   ---   - Visual Basic Help   - Visual Basic Tutorials   - Visual Basic Submissions     Education ----------- - Student Life   Graphics and Design ----------------------- - Web Design     Web Design Forums   ---------------------   - (X)HTML Help   - (X)HTML Tutorials   - Flash MX Help   - Flash MX Tutorials   - Graphics     Graphics Forums   ------------------   - Photoshop Tutorials   - The Showroom   - 2D Graphics   - 3D Graphics     Teams ------ - dTeam Public

 Style: Appalachia blueSilver eMJay subAppalachia subBlue subCanvas subEmjay subGrey subSilver subVereor Search: