Computer Science Canada [Tutorial] From Turing to O'Caml - Days 1 through 14 |
Author: | wtd [ 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.
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.
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
Note that the order of operations you are used to is maintained. Operators are not overloaded, as you will see below.
Day 3 Giving names to things In Turing, you'd write something like the following.
In O'Caml, we'd do much the same with the following (in the toplevel).
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.
Now, experiment with other names and other values. Day 4 Functions Let's start simple.
Here the types involved in the function "foo" are explicitly delimited.
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.
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.
And let's print something.
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:
This expression may seem unwieldy. Let's use local bindings to clean it up.
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.
And now in O'Caml.
Functions with multiple expresions Let's organize that bit of code.
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
Day 7 Conditionals
A different take
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.
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.
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.
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:
If you're using Windows, you should have it tack a ".exe" onto your executable name.
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.
Let's add a few functions in.
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?
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.
Now, our main program:
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.
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...
Or we can even handle this locally.
Day 10 Let's create a linked list Untested
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
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.
We need a function to determine if the node has a next value. We'll use pattern matching.
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.
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.
Sure enough, if I try this in the interpreter:
Of course, that function only really needs one terminating condition.
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?
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.
Now, let's rewrite this function to use an accumulator.
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.
Whoa! What's that inferred type signature mean?
Two types are involved, 'a and 'b. These are generic types. They can be anything. The fold_list function takes three arguments:
and:
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.
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.
A convenience Having to name a function for this purpose is inconvenient.
A few uses One sum function:
Or...
A function to determine if a list contains a given value.
What if we want a nice way to print a list?
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.
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.
Day 13 Object-oriented programming Let's take a code-heavy approach. A very simple class. Turing:
O'Caml:
Let's create a new object. Turing:
O'Caml:
Let's add a method.
o'Caml:
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:
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:
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.
But that's really quite pointless unless we alter the value of name at some point.
Interfaces Now, let's magine a function which takes a Foo object as an argument.
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.
O'Caml takes a bit of a different approach. We've seen a lot of type inferencing so far. Let's see it again.
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. |
Author: | Saad [ 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 . |
Author: | rizzix [ Mon Jul 07, 2008 1:26 am ] |
Post subject: | RE:[Tutorial] From Turing to O\'Caml - Days 1 through 4 |
Nice work wtd! |
Author: | michaelp [ 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! |
Author: | wtd [ Sat Jul 12, 2008 3:11 pm ] |
Post subject: | Re: [Tutorial] From Turing to O'Caml - Days 1 through 8 |
Bump for edits. |
Author: | michaelp [ Sat Jul 12, 2008 3:31 pm ] |
Post subject: | RE:[Tutorial] From Turing to O\'Caml - Days 1 through 9 |
Day 8 is repeated. |
Author: | rdrake [ 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. |
Author: | michaelp [ 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. |
Author: | wtd [ 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. |
Author: | Saad [ 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 |
Author: | wtd [ 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. |
Author: | Zampano [ Thu Jul 17, 2008 6:06 am ] |
Post subject: | Re: [Tutorial] From Turing to O'Caml - Days 1 through 11 |
That's very thorough. |
Author: | michaelp [ 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! |
Author: | wtd [ 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. |
Author: | wtd [ 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? |
Author: | Clayton [ Sat Jul 26, 2008 9:02 am ] |
Post subject: | RE:[Tutorial] From Turing to O\'Caml - Days 1 through 14 |
Simply amazing as usual wtd. Definitely something I need to take a better look at when I have the time. |
Author: | michaelp [ Wed Aug 13, 2008 11:01 am ] |
Post subject: | RE:[Tutorial] From Turing to O\'Caml - Days 1 through 14 |
Okay, I'm ready to try! Any good editors for O'Caml? I have notepad++, but there was only 'Caml', are they the same things? |
Author: | wtd [ Wed Aug 13, 2008 1:26 pm ] |
Post subject: | RE:[Tutorial] From Turing to O\'Caml - Days 1 through 14 |
O'Caml is essentially an object-oriented extension to Caml. Should work well enough. But seriously, the toplevel is a phenomenal tool. Use it. |