PDA

View Full Version : O'Caml introduction


GnuVince
05-28-2002, 08:47 PM
Small introduction to O'Caml

O'Caml is a computer programming language. Its homepage is at:
http://caml.inria.fr

O'Caml is truely a wonderful language once you know it, but the first
steps are VERY hard, and hopefully this tutorial will make things
easier. First off, O'Caml is not a procedural language: it belongs in
the functional languages family. Pure functional languages provide no
loop mecanism (you loop using recursion), once set, variables cannot be
altered, etc. Fortuneatly, O'Caml is not a pure functionnal language,
and loops and mutable variables are available. So let's start!

First, start the O'Caml top-level loop my running `ocaml' at your
command prompt, you should see something like this:


Objective Caml version 3.04

#


The # is the O'Caml prompt, so you are ready to start.



1. Types

O'caml is language that is strictly typed: this means that once set, a
variable cannot change types, and you cannot do operations between two
type (e.g: adding an integer and a float). With this in mind, let's try
to do some basic maths:


# 2 + 2;;
- : int = 4


the ';;' is used to say when an expression is finished, it's like a
period in a phrase. The second line shows the result (4) and the type
of the result (int). I was talking that types cannot be mixed, let's
see what happens when we try


# 2 + 2.5;;
This expression has type float but is here used with type int


2.5 is underlined and you have the error message on the second line.
What does it tell you? Well, 2.5 which is of type float is used with a
function that only manipulates integers (+). So how do you add them?
The addition operator for floats it '+.' (a plus with a dot after to
denote that it works with floats). So if we try our operation again,
but with +., we get:


# 2 +. 2.5;;
This expression has type int but is here used with type float


Now the 2 is underlined and ocaml complains that an int is being used
with a float operator. So how are we going to make this sum? Well, we
need to convert one of the numbers to the type of the other. If we
convert 2.5 to int, we lose the .5 (which is important), so we'll
convert 2 to be a float.


# (float_of_int 2) +. 2.5;;
- : float = 4.5


What is float_of_int? Let's ask ocaml!


# float_of_int;;
- : int -> float = <fun>


O'caml tells us that float_of_int is a function (<fun>) that takes an
integer argument (int) and returns a float value. So when you gave
float_of_int the argument 2, it returned the same thing, but with type
float so that the operation could be made.

So remember to always work the same type!

List of Arithmetic operators for integers:
+ : Addition
- : Substraction
* : Multiplication
/ : Division
mod : Modulo

List of Arithmetic operators for floats:
+. : Addition
-. : Substraction
*. : Multiplication
/. : Division
** : Exponentiation

To convert an integer to a float: float_of_int n
To convert a float to an integer: int_of_float n

That will be all for now. Next time: variables

Ninja40
07-20-2003, 01:46 PM
Well, since Gnuvince has been asleep for a while :D, I continue with functions.

Hey, after all, we're talking about functional programming, aren't we ?

So let's start with a very gentle inroduction to the concepts.



Defining a function

In Ocaml, like in other strongly typed languages, a function takes parameters of a predefined types and returns a value of a given type. The parameters lare simply separated by a space character.

Let's say we want to define the mathematical functions :

double : x -> 2X
square : x -> x*x
cube : x -> x^3


Here is how it is written in Ocaml :

# let double = fun x -> 2. *. x ;;
# let square = fun x -> x *. x ;;
# let cube = fun x -> x ** 3. ;;

In this syntax, the function definition is really the part which starts with "fun". The part "let double = " merely gives a name to the function. It is possible to use unnamed functions, as we'll see below.

Alternative syntax

# let double x = 2. *. x ;;
# let square x = x *. x ;;
# let cube x = x ** 3. ;;

Note the dot after the * symbol. This tells the compiler that the function accepts only type floats (and therefore, returns a float). Without them, the function square would accept only integers. This processus of guessing the types using the syntax is called type inference. It is very useful as it makes programs much easier to read : programs are not clutered with local vriables. In order to avoi misunderstanding, the Ocaml interpreter always tells you what types it has infered. I won't reproduce the output, though, for clarity.

functions are first-class types
Ok, nothing really new here. What is more interesting is the fact that in Ocaml, functions are first-class types, that is, they can be passed to functions and returned just like any classic data type (integers, floats, strings...).

Let's illustrate this in an example. Suppose we want to define the composite function of two functions f and g like this :

f o g : x -> (f o g) (x)= f(g(x))

In Ocaml, this is simply :
let compose f g = fun x -> f(g(x)) ;;
If you are careful, you'll notice several things :
1. the syntax is very close to the mathematical definition above.
2. I didn't need to give x as a variable to the function compose, I merely gave f and g as parameters. The Ocaml type checker guesses f and g MUST be functions because of the x -> f(g(x)) syntax. The x variable is a dummy variable, called a bound variable.
3. the function compose actually returns a function, which is the composite of f and g, as we can see below :

# compose double square 3. ;;
- : float = 18.

# compose square double 3.;;
- : float = 36.

(* Define a composite with a function that divides its input by 2 *)
# let myfunc = compose square (fun x -> x /. 2.) ;;
# myfunc 5. ;;
- : float = 6.25

Here, I defined myfunc as being a composite of the square function and an unnamed function that divides its input by 2.

To show that the function compose returns a function, I could have as well written this :

(* Define a composite with a function that divides its input by 2 *)
# let myfunc f = compose square f ;;
# myfunc (fun x -> x /. 2.) 5. ;;
- : float = 6.25


Operators
One can define an infix operator for the compose function. An operator may not contain letters. To make it infix, one has to put it between parentheses.
Here is how to do it in Ocaml :


(* Define the infix operator composite *)
# let (@) = compose ;;

# (double@square) 3. ;;
- : float = 18.

# (square@double) 3.;;
- : float = 36.

Question : why do we need parentheses around square@double ?

Functions that work on lists
The list is a standard data structure in functional languages.
One can easily apply a function to all the elements of a list using the function of the standard library List.map, without any need for a loop.


(*create a list of floats *)
# let v = [1.; 2.; 3.; 4.; 5.] ;;

# List.map (myfunc@square) v;;
- : float list = [0.25; 4.; 20.25; 64.; 156.25]


In order to avoid having to write List.map, one could also rewrite the whole thing like this :

(* These new functions take a list of floats as their input and return a list of floats *)
# let square= fun v -> List.map (fun x-> x *. x) v ;;

# let myfun = fun v -> List.map (square@(fun x -> x /. 2.)) v ;;

# (myfunc@square) v;;
- : float list = [0.25; 4.; 20.25; 64.; 156.25]

Smerdyakov
07-20-2003, 01:53 PM
There is an O'Reilly book about OCaml available in its entirety online at http://caml.inria.fr/oreilly-book/html/index.html. Do you think it's worth continuing the exposition here when there's a nice big book available? :-)

DNAunion2000
07-20-2003, 02:21 PM
DNAunion: So far, looks harder to work with than C++.

C++ coerces (automatically promotes) variables to the same data type for you in mixed expressions. For example:

float x = 2 + 2.5;

C++ temporarily converts the integer 2 into the float 2.0, behind the scenes.

Also, you can pass functions into other functions in C++. You simply pass the function name as an argument (C++ treats it as a pointer to the function...voila!).

Also, you can easily create composite functions in C++, like so.

fog(int x) {return f(g(x));}

Smerdyakov
07-20-2003, 02:30 PM
Automatic casts are viewed as a _negative_ point by the ML community. Like overloaded functions and operators, they make it harder to tell what your code actually does.

C++ supports function pointers, but not _closures_. Also notice that you have to make a new function definition just to compose two functions.

Further explanation of what a closure is, etc., can be found at a page I created about ML (Standard ML, in this case), http://www.hprog.org/fhp/MlLanguage.

Ninja40
07-20-2003, 03:35 PM
Originally posted by DNAunion2000
DNAunion: So far, looks harder to work with than C++.

C++ coerces (automatically promotes) variables to the same data type for you in mixed expressions. For example:

float x = 2 + 2.5;

C++ temporarily converts the integer 2 into the float 2.0, behind the scenes.


And that's bad !
ML forbids such conversions as it is statically, strongly typed.


Also, you can pass functions into other functions in C++. You simply pass the function name as an argument (C++ treats it as a pointer to the function...voila!).

Also, you can easily create composite functions in C++, like so.

fog(int x) {return f(g(x));}

Yup, you can use function pointers, but a function won't return a function, merely a value. That is, you can't create a specialized function by passing only some parameters to your function like I did.

Also, in your last example, you have to define a new function for every composition. You should have defined a new C++ operator, like I did.

Smerdyakov
07-20-2003, 03:38 PM
Not to be rude, but you have exactly repeated my last post, modulo rewording. Any reason? ;-)

Ninja40
07-20-2003, 04:05 PM
Well, I didn't read your post. Anyway, what do you think of my little tutorial of functions ? I wrote it in a way to show features that I thought were specific to functional programming.

Smerdyakov
07-20-2003, 04:09 PM
It's fine, but I prefer the overall ML advocacy page I've linked to 2 posts back.

DNAunion2000
07-20-2003, 06:26 PM
DNAunion: Well, obviously I know nothing about your language except for the few words posted above prior to my post.

Perhaps if I saw it in action. Here's something that a beginning C++ programmer is working on in a different thread. Perhaps you could show how to do it in your language so that I can compare the two.

Here's what he is working on. To create a program that prompts the user for a 4-digit number that the program then "encrypts". "Encryption" should be carried out by: (1) on a digit by digit basis, adding 7 to the digit, getting the remainder of dividing that result by 10, and storing that result back into that digit, and (2) swapping modified digits 1 and 3, and 2 and 4. The program should then display the "encrypted" result to the user, and finally ask the user to enter another 4-digit number to encrypt (the user can enter 0 to end the program).

Smerdyakov
07-20-2003, 06:29 PM
DNA, have you read the page at the URL I posted, http://www.hprog.org/fhp/MlLanguage? That should explain things better than the example you have requested.

P.S.: Please stop beginning every message with "DNAunion".

Ninja40
07-21-2003, 06:27 PM
>> It's fine, but I prefer the overall ML advocacy page I've linked to 2 posts back. <<

Oh yes, I've seen it. I agree with you, you did a great job, it's very good.