Computer Science Canada O'Caml for OO Programmers |
Author: | wtd [ Fri May 06, 2005 12:06 am ] | ||||||||||||||||||||||||
Post subject: | O'Caml for OO Programmers | ||||||||||||||||||||||||
O'Caml: A Powerful Option for OO Programming So far I've mostly talked up O'Caml on its merits as a functional language, but the "O" in the name is shorthand for "Objective" and O'Caml has powerful support for object-oriented programming, and offers some conveniences programmers who work in other OO languages would do well to heed. Good Habits Primary among O'Caml's benefits for OO programmers is the fact that all instance variables are private, and methods are public by default. Differing from other languages, private methods and instance variables are inherited, but remain private. As such, there is no "protected" qualifier. Also handy is that private methods can be made public easily via a type signature. Inheritance Convenience Let's consider how we can expose protected methods from a parent class.
Now, a naive O'Caml scenario.
But even better, we can use a type signature for "self".
Naming Flexibility O'Caml's class syntax doesn't lock you into a particular set of names. Let's say we want "this" as in C++ and Java.
Of course, it's also possible to ellide this entirely. Due to the signature we use in the "bar" class, this isn't possible.
We also can't remove these names if we want to access methods from within methods.
The names of inherited classes can be aliased as well.
Other Good Habits: Instance Variables Let's create a class with an instance variable. In this case it's a greeter class which contains an instance variable to hold the name of the person to greet. Methods are available to get a name, and to greet the person. If a name hasn't been read, raise a No_name exception.
But there's a problem. Instance variables by default in O'Caml are immutable ("constant"). We need to very specifically make the instance variable mutable. This, though, is a very good thing. Many times, what programmers really want are constant values, and we often forget to include "const" or "final" where appropriate.
Initializing Instance Variables In that last example I simply set an instance variable equal to an empty string. Not a lot of flexibility there. So, how do I set some other value?
Now, we can create a new name.
And then we can print the full name.
Comments? Feel free to ask any questions you may have. This is not by any means comprehensive. |
Author: | wtd [ Sat May 07, 2005 7:33 pm ] | ||||||
Post subject: | |||||||
What's Missing? It should be fairly obvious to anyone familiar with other OO languages that so far I have talked about constructors. Yes, I can pass arguments to a class at creation, but I can't do anything else with those values. So let's look at a simple "bank account" class.
The problem: what if we create a bank account with an initial balanace that's a negative number? We have no way of enforcing that yet, but we can enforce it, by using an initializer.
Inheritance and Initializers Initiaizers are inherited, sso, in this instance any class that inherits bank_account would have to enforce that condition. As such, it might be better to place this logic in a subclass.
The Benefit The fact that initializers are inherited may seem to be an inconvenience, but it does allow for enforced conditions. Such strict enforcement is not in place in many other OO programming languages, especially if they use protected instance variables which behave the same way O'Caml's instance variables do. |
Author: | wtd [ Wed May 11, 2005 6:01 pm ] | ||||||||||||||||
Post subject: | |||||||||||||||||
Class Interfaces In Java, and other programming languages, we have classes, but we also have interfaces, which only specify that a class implementing that interface must include certain things, but don't specify those methods themselves. O'Caml has these as well.
An implementing class then has to have those methods.
Now we can use the interface as a type, and ensure that we only receive types which implement that interface.
However, OCaml throws us a twist. It's type inferencing nature meanswe can take a shortcut.
This clearly indicates that "obj" must have bar and baz methods, and that they must return an int and a string respectively. O'Caml knows this at compile-time. As a result, it will reject any object at compile-time which doesn't implement those methods. In place of the above, we could simply write:
The interface remains handy because it can prevent us from compiling a class which doesn't implement certain methods. Next Up Abstract classes. Stay tuned. ![]() |
Author: | wtd [ Thu May 12, 2005 1:12 am ] | ||||||||
Post subject: | |||||||||
Abstract Classes Let's look at a simple interface for account classes.
Now, let's implement this in a saving account class which provides 3% interest.
But here we can see that there's redundancy. Both the withdraw and pay_interest methods can be defined entirely in terms of other methods. As a result, there's no reason we shouldn't define them in the interface, so that classes which implement "bank_account" won't have to implement those methods. Fortunately the syntax remains familiar.
And now savings account can be defined as:
|
Author: | wtd [ Fri May 13, 2005 5:58 pm ] | ||||||||||||
Post subject: | |||||||||||||
Recap We've seen that interfaces and abstract classes in O'Caml are really the same thing, with abstract classes simply providing some default method definitions. Inheritance Inheritance? Again? Sure why not... The fun thing is... interfaces and abstract classes can be extended in O'Caml. Let's look at an example.
This abstract class provides for an "equals" method which determines equality between two objects. The "not_equals" method is defined by default. Due to the nature of O'Caml's type system, we can't actually refer to a class by name within the class. We can, however, use a type signature to specify that "self" is the generic type 'a.
And here we've created a very simple class which implements that abstract class. Now, what if we want to do a three way comparison. "Less than" returns -1, "equals" returns 0, and "greater than returns 1.
Now, I can implement it alongside "equalable" for the same simple class.
But this is really quite messy. Clearly implementing the comparable interface implies that I've implemented equalable. Fortunately we can directly express that in O'Caml, by having comparable inherit equalable.
My class can then be simplified to:
|
Author: | wtd [ Sat May 14, 2005 5:37 pm ] | ||||||||||||||
Post subject: | |||||||||||||||
Singletons Those used to Java probably know this ideas better as "anonymous inner classes". So, can O'Caml do this or something effectively the same? As of version 3.08 the answer is most definitely yes. Let's consider a simple Java example. I have an interface for anything that can speak.
As we've discusses we can replicate this in O'Caml.
Now let's have a method which takes a Speaker object.
And in O'Caml:
Now, in Java we can use an anonymous inner class if we don't want to create a class which implements Speaker.
And in O'Caml:
Consistency Some might look at O'Caml and see a mish-mash of inconsistency. In places they'd be write. But here there's consistency. An anonymous object is no different from a class. Type Inferencing Due to the type system of O'Caml it isn't even really necessary to specify that "s" should be a "speaker". The compiler will see that the "speak" method is called, and only allow the program to compile if the object being passed in implements that method. Our code could simply be:
|