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

Username:   Password: 
 RegisterRegister   
 For Cervantes: quick O'Caml tour
Index -> Programming, General Programming -> Functional Programming
Goto page 1, 2  Next
View previous topic Printable versionDownload TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: Thu Oct 27, 2005 7:58 pm   Post subject: For Cervantes: quick O'Caml tour

Conditionals

The basic conditional looks like this:

code:
if condition then expression1 else expression 2


The "else" can be optional, but only if expression1 returns unit. For instance:

code:
# if true then print_endline "hello";;
hello
- : unit = ()
# if true then 42;;
Characters 13-15:
  if true then 42;;
               ^^
This expression has type int but is here used with type unit


Since O'Caml is statically typed, both expression1 and expression2 must have the same type.

code:
# if true then 42 else 27;;
- : int = 42
# if true then 42 else 'z';;
Characters 21-24:
  if true then 42 else 'z';;
                       ^^^
This expression has type char but is here used with type int


But, what if you want multiple expressions to be evaluated?

Both expression1 and expression2 can only be a single expression. However, grouping multiple expressions together with "begin...end" or parentheses makes them one big expression.

code:
# if true then begin
    print_string "foo ";
    print_int 42;
    print_endline " bar"
  end
  else (
    print_endline "wooble!";
    print_endline "Ninja!"
  );;
foo 42 bar
- : unit = ()


What if I want "else if"?

Well, as noted previously, expression1 and expression2 can only be a single expression. Well, here's a surprise for you: an entire if...then...else..." thing-a-ma-jig is a single expression.

code:
# if true then 42 else if true then 35 else 27;;
- : int = 42


Another example of this concept of conditionals as a single expression in action:

code:
# if true then print_endline "Hello" else print_endline "world";;
Hello
- : unit = ()
# print_endline (if true then "Hello" else "world");;
Hello
- : unit = ()


Loops

Loops in O'Caml are deviously simple. You won't use them much as you become more familiar with the language, though.

First we have the basic "for" loop. It increments from a starting value to an terminating value and evaluates a set of expressions each time.

code:
# let my_array = [|1; 45; 16; 2|] in
    for index = 0 to Array.length my_array - 1 do
      print_int my_array.(index);
      print_newline ()
    done;;
1
45
16
2
- : unit = ()


We also have a "while" loop.

code:
# let my_array = [|1; 42; 65; 54; 9|]
  and counter = ref 0
  in
    while !counter < Array.length my_array do
      print_int my_array.(!counter);
      print_newline ();
      counter := !counter + 1
    done;;
1
42
65
54
9
- : unit = ()


Why wouldn't you use these? Because they can't compare to the beauty of:

code:
# let my_array = [|1; 42; 65; 54; 9|] in
    Array.iter (fun x -> print_int x; print_newline ()) my_array;;
1
42
65
54
9
- : unit = ()


Or even better:

code:
# let my_array = [|1; 42; 65; 54; 9|] in
    Array.iter (Printf.printf "%d\n") my_array;;
1
42
65
54
9
- : unit = ()


Or perhaps:

code:
# let my_array = [|1; 42; 65; 54; 9|]
  and print_func = Printf.printf "%d\n"
  in
    Array.iter print_func my_array;;
1
42
65
54
9
- : unit = ()


Pattern Matching

This often takes the form of something similar to the famous "switch" or "case".

Let's look at an example:

code:
- : unit = ()
# let a = 42 in
    match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven"
     | _  -> "Some other funky number";;
- : string = "Some other funky number"


The underscore means "anything... I don't really care." If I did care, I could use an actual name.

code:
# let a = 42 in
    match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven"
     | n  -> string_of_int n;;
- : string = "42"


Again, since O'Caml is statically typed, the expression being evaluated (in the above case "a") will always be of one specific type. That means all of the patterns have to be of the same type. Try to mix ints and chars, for instance, and it won't run.

code:
# let a = 42 in
    match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven"
     | 's' -> "s"
     | n -> string_of_int n;;
Characters 83-86:
     | 's' -> "s"
       ^^^
This pattern matches values of type char
but is here used to match values of type int


If we try to write a non-exhaustive matching, O'Caml does a good job of pointing out this potentially huge source of problems. That it catches this at that point is beautiful. Other languages would not say a word and then try to clean up the mess later when it actually becomes a problem.

code:
# let a = 42 in
    match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven";;
Characters 16-77:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
0
  ..match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven"..
Exception: Match_failure ("", 52, 2).
# let a = 32 in
    match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven";;
Characters 16-77:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
0
  ..match a with
       32 -> "Thirty-two"
     | 47 -> "Forty-seven"..
- : string = "Thirty-two"


Tuples

Consider tuples to be a fixed-length, immutable, heterogenous list.

A simple tuple might hold a set of coordinates in three dimensional space.

code:
(34, 57, 82)


Every tuple has a very specific type, which is represented by the types of its components, separated by asterisks.

code:
# (34, 57, 82);;
- : int * int * int = (34, 57, 82)


We can use tuples in matching.

code:
# match (34, 57, 82) with
     (34, 57, 82) -> "Confound you Bond!  You found my doomsday weapon!"
   | (34, 57, n)  -> "Close, but your personal jetpack doesn't have enough gas."
   | _            -> "Good luck next time, double-oh-loser!";;
- : string = "Confound you Bond!  You found my doomsday weapon!"


code:
# match (34, 57, 74) with
     (34, 57, 82) -> "Confound you Bond!  You found my doomsday weapon!"
   | (34, 57, n)  -> "Close, but your personal jetpack doesn't have enough gas."
   | _            -> "Good luck next time, double-oh-loser!";;
- : string = "Close, but your personal jetpack doesn't have enough gas."


Pattern matching also applies to "let" bindings, and this is one place it's phenomenally useful (among others).

code:
# let a, b, c = 34, 57, 82;;
val a : int = 34
val b : int = 57
val c : int = 82


The comma operator creates a tuple. It should be noted, that if you want to be as clear as possibly, surround the tuple with parentheses.

code:
# let (a, b, c) = (34, 57, 82);;
val a : int = 34
val b : int = 57
val c : int = 82


I take exception to that!

In that last match example you saw an exception: Match_failure.

So, how do we handle exceptions in O'Caml? Why, with the "try" keyword, of course.

code:
# try (let a = 42 in
    match a with
       32 -> "thirty-two"
     | 47 -> "forty-seven")
  with Match_failure _ -> "some other number";;
Characters 21-82:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
0
  ..match a with
       32 -> "thirty-two"
     | 47 -> "forty-seven".
- : string = "some other number"


This is very similar to "match...with...". The first thing to notice is that an entire "match...with..." is a single expression.

So, we try an expression, and when that fails, we handle the exception with something that looks very much like pattern matching. An exception in O'Caml can have one or zero values associated with it. If you'd like multiple values, you can tie them together into a single tuple.

Match_failure does that, and we can pattern match on that tuple. Or we can say we don't give a flying rat's butt and just use underscore as a pattern that matches anything.

We could also just replace the entire exception with an underscore, so that it handles any exception.

code:
# try (let a = 42 in
    match a with
       32 -> "thirty-two"
     | 47 -> "forty-seven")
  with _ -> "some other number";;
Characters 21-82:
Warning: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
0
  ..match a with
       32 -> "thirty-two"
     | 47 -> "forty-seven".
- : string = "some other number"


We can easily create our own exceptions.

code:
# exception A;;
exception A
# raise A;;
Exception: A.
# exception B of string;;
exception B of string
# raise (B "Hello");;
Exception: B "Hello".
# exception C of string * int;;
exception C of string * int
# raise (C ("foo", 42));;
Exception: C ("foo", 42).


Lists

List are immutable, variable length, homogenous collections. They can be easily represented in code in literal form.

code:
# [1; 2; 3];;
- : int list = [1; 2; 3]


Perhaps the best thing about lists has to do with the :: operator, and how it realtes to pattern matching.

The :: operator will take a value, and a list, and create a new list with that value on the front.

code:
# 1 :: [2; 3];;
- : int list = [1; 2; 3]


Lists are inherently recursive data structures.

The previous example could be expressed as:

code:
# 1 :: 2 :: [3];;
- : int list = [1; 2; 3]


Or, going further:

code:
# 1 :: 2 :: 3 :: [];;
- : int list = [1; 2; 3]


We see here that a list is either an empty list, or some value tacked onto the front of a list. We can use this fact when we pattern match.

code:
# let lst = [1; 2; 3] in
     match lst with
        []   -> print_endline "Empty list."
      | x::_ -> print_endline (string_of_int x);;
1
- : unit = ()


code:
# let lst = [] in
     match lst with
        []   -> print_endline "Empty list."
      | x::_ -> print_endline (string_of_int x);;
Empty list.
- : unit = ()


You'll notice that I used underscore again to represent the rest of the list, since I had no use for that information.

Functions

Since we've looked at a recursive data structure (lists), it's about time to look at functions, and eventually recursive functions.

All functions must take at least one argument, which if nothing else is () or "unit".

code:
# let say_hello () = print_endline "Hello";;
val say_hello : unit -> unit = <fun>
# say_hello ();;
Hello
- : unit = ()


Functions can, however, take multiple arguments.

code:
# let add a b = a + b;;
val add : int -> int -> int = <fun>
# add 1 3;;
- : int = 4


We can also have multiple expressions inside a function.

code:
# let add a b = print_endline "add function"; a + b;;
val add : int -> int -> int = <fun>
# add 1 3;;
add function
- : int = 4


There's much much more to say about functions, but first let's look at recursive functions a bit.

code:
# let say_hello_forever () =
    print_endline "Hello";
    say_hello_forever ();;
Characters 55-72:
    say_hello_forever ();;
    ^^^^^^^^^^^^^^^^^
Unbound value say_hello_forever


The reason this code wouldn't work is that for normal functions, the function itself doesn't know about the name you've given to it. This would work if we'd previously created a "say_hello_forever" function, but in that case it would be calling the old "say_hello_forever" function.

code:
# let a () = 42;;
val a : unit -> int = <fun>
# let a () = print_endline "a"; a ();;
val a : unit -> int = <fun>
# a ();;
a
- : int = 42


We can fix this with the "rec" keyword.

code:
# let rec say_wooble_forever () =
    print_endline "wooble";
    say_wooble_forever ();;
val say_wooble_forever : unit -> 'a = <fun>


Of course, that's silly. We don't want infinite loops, so we need a way to tell the function to stop calling itself. A conditional or match will work just fine for this.

code:
# let rec print_range a b =
    if a > b then ()
    else (
      print_endline (string_of_int a);
      print_range (a + 1) b
    );;
val print_range : int -> int -> unit = <fun>
# print_range 1 5;;
1
2
3
4
5
- : unit = ()


But it's silly to have recursive functions that just "do" things rather than returning some useful value. Remember how I mentioned that lists are inherently recursive, and that :: can be used to create lists?

code:
# let rec range a b =
    if a > b then []
    else a :: range (a + 1) b;;
val range : int -> int -> int list = <fun>
# range 1 5;;
- : int list = [1; 2; 3; 4; 5]


If we step through what's happening in "range 1 5" we see:

code:
range 1 5
1 :: range 2 5
1 :: 2 :: range 3 5
1 :: 2 :: 3 :: range 4 5
1 :: 2 :: 3 :: 4 :: range 5 5
1 :: 2 :: 3 :: 4 :: 5 :: range 6 5
1 :: 2 :: 3 :: 4 :: 5 :: []
[1; 2; 3; 4; 5]


Variant Types

Let's look at a variant type. Let's say I want my own version of the standard list. I'll have it store ints.

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


Using this is a piece of cake, since Node and Empty are the necessary constructors.

code:
# Node (4, Node (5, Node (7, Empty)));;
- : my_list = Node (4, Node (5, Node (7, Empty)))


Then writing a recursive function to deal with this is a piece of cake.

code:
# let rec to_list lst =
    match lst with
       Empty          -> []
     | Node (v, lst2) -> v :: to_list lst2;;
val to_list : my_list -> int list = <fun>
# let l = Node (4, Node (5, Node (7, Empty))) in
    to_list l;;
- : int list = [4; 5; 7]


Of course, the built-in list can work on anything... not just ints.

code:
# type 'a my_list = Node of 'a * 'a my_list | Empty;;
type 'a my_list = Node of 'a * 'a my_list | Empty
# let rec to_list lst =
    match lst with
       Empty -> []
     | Node (v, lst2) -> v :: to_list lst2;;
val to_list : 'a my_list -> 'a list = <fun>
# let l = Node ('r', Node ('z', Node ('a', Empty))) in
    to_list l;;
- : char list = ['r'; 'z'; 'a']


The "'a" in the above indicates a generic parameter. It can stand in for any type.

The function to_list nicely demonstrates pattern matching and variant types.
Sponsor
Sponsor
Sponsor
sponsor
Naveg




PostPosted: Thu Oct 27, 2005 8:12 pm   Post subject: (No subject)

Great intro!

And just in time, grab your copy of 3.09.0, released today!

http://caml.inria.fr/download.en.html
wtd




PostPosted: Thu Oct 27, 2005 8:15 pm   Post subject: (No subject)

Thanks for the link.
Cervantes




PostPosted: Fri Oct 28, 2005 5:33 pm   Post subject: (No subject)

Heart

Thank you, wtd!
I shall read it soon.
wtd




PostPosted: Fri Oct 28, 2005 6:28 pm   Post subject: (No subject)

Commenting

code:
(* Hello, world! *)


Oh, and they nest, unlike C-style comments.

code:
(* Hello (* world *)*)
wtd




PostPosted: Sat Oct 29, 2005 1:13 pm   Post subject: (No subject)

Mutable Values

O'Caml, by default, makes things constant... immutable.

O'Caml is not a pure functional programming language, though, and also offers the ability to create mutable values.

I've used this previously in a loop, to have a mutable counter.

code:
# let my_array = [|1; 42; 65; 54; 9|]
  and counter = ref 0
  in
    while !counter < Array.length my_array do
      print_int my_array.(!counter);
      print_newline ();
      counter := !counter + 1
    done;;
1
42
65
54
9
- : unit = ()


If I look at "ref 0" a bit closer...

code:
# let counter = ref 0;;
val counter : int ref = {contents = 0}


So the "ref" function created a new reference to zero. How can we manipulate this?

The prefix ! operator extracts the value from the reference.

code:
# !counter;;
- : int = 0


The := operator, which looks familiar from a lot of different programming languages, mutates the value in the reference.

code:
# counter := 42;;
- : unit = ()
# !counter;;
- : int = 42


Thus incrementing the reference:

code:
# counter := !counter + 1;;
- : unit = ()
# !counter;;
- : int = 43
wtd




PostPosted: Sat Oct 29, 2005 1:24 pm   Post subject: (No subject)

Record Types

So, how does "ref" work? Is it magic?

Nope. It's a record, which is why this topic is overdue for discussion.

Let's say we want to reinvent "ref". We need a record with a single field.

code:
# type 'a rref = { rcontents : 'a };;
type 'a rref = { rcontents : 'a; }


Oh, and we'll want a function to create a new rref.

code:
# let rref value = { rcontents = value };;
val rref : 'a -> 'a rref = <fun>


And a function to retrieve the value stored in it.

code:
# let deref r = r.rcontents;;
val deref : 'a rref -> 'a = <fun>


Now we can actually create a rref.

code:
# let foo = rref 42;;
val foo : int rref = {rcontents = 42}
# deref foo;;
- : int = 42


Now, how do I mutate that value? I'd need a "setref" function.

code:
# let setref r new_value = r.rcontents <- new_value;;
Characters 25-49:
  let setref r new_value = r.rcontents <- new_value;;
                           ^^^^^^^^^^^^^^^^^^^^^^^^
The record field label rcontents is not mutable


That's not quite right. It didn't work because, as the error message explains, rcontents is not mutable. Let's make it mutable.


code:
# type 'a rref = {mutable rcontents:'a};;
type 'a rref = { mutable rcontents : 'a; }
# let rref v = {rcontents = v};;
val rref : 'a -> 'a rref = <fun>
# let setref r v = r.rcontents <- v;;
val setref : 'a rref -> 'a -> unit = <fun>
# let deref r = r.rcontents;;
val deref : 'a rref -> 'a = <fun>
# let foo = rref 0;;
val foo : int rref = {rcontents = 0}
# deref foo;;
- : int = 0
# setref foo 42;;
- : unit = ()
# deref foo;;
- : int = 42
wtd




PostPosted: Sat Oct 29, 2005 6:04 pm   Post subject: (No subject)

;;

There have been lots of these in the above examples.

The O'Caml toplevel (interactive interpreter) requires them so it knows when you're done entering code. However, in a complete program you're far less likely to need them.

The "let" keyword is a fairly common one in O'Caml, and is a powerful separator.

We can see some of this in the toplevel.

code:
# let a = 42
  let b = 27

  let foo () =
    print_endline "Hello";
    print_endline "world"

  let baz = 3.14;;
val a : int = 42
val b : int = 27
val foo : unit -> unit = <fun>
val baz : float = 3.14


There are places where this doesn't work, though.

code:
# let wooble () = print_endline "wooble"
  wooble ();;
Characters 16-29:
  let wooble () = print_endline "wooble"
                  ^^^^^^^^^^^^^
This function is applied to too many arguments,
maybe you forgot a `;'


In the above, O'Caml thinks you're trying to pass "wooble" and "()" to "print_endline".
Sponsor
Sponsor
Sponsor
sponsor
wtd




PostPosted: Tue Nov 01, 2005 12:46 pm   Post subject: (No subject)

File I/O

Let's open a text file for reading:

code:
# let file_handle = open_in "test.ml";;
val file_handle : in_channel = <abstr>


Let's read a line from it:

code:
# input_line file_handle;;
- : string = "open Graphics;;"


And then we'll close it:

code:
# close_in file_handle;;
- : unit = ()


Let's open a file for output, write to it, then close it:

code:
# let file_handle = open_out "foo.txt";;
val file_handle : out_channel = <abstr>
# output_string file_handle "Hello, world!";;
- : unit = ()
# close_out file_handle;;
- : unit = ()


Now, how do we read all of the lines in a file? Well, recursion and exception handling gives us all the tools we need.

First let's see what happens when we try to read from a file indefinitely.

code:
# let file_handle = open_in "test.ml";;
val file_handle : in_channel = <abstr>
# while true do
    let line = input_line file_handle in
      print_endline line
  done;;
open Graphics;;

let count = ref 0;;

open_graph " 640x480";;

while true do
   if button_down () then
      let (x, y) = mouse_pos () in
      let coords = string_of_int x ^ "x" ^ string_of_int y in
         begin
            moveto x y;
            draw_string coords
         end;
      count := !count + 1
done
Exception: End_of_file.


When we try to read past the end of the file, an End_of_file exception is thrown.

So, let's write a simple recursive function to read all lines.

code:
# let rec read_all_lines file_handle =
    let line = input_line file_handle in
      line :: read_all_lines file_handle;;
val read_all_lines : in_channel -> string list = <fun>
# let file_handle = open_in "test.ml";;
val file_handle : in_channel = <abstr>
# read_all_lines file_handle;;
Exception: End_of_file.


Same thing, because we never handled the exception. Plus, in our recursive function we never specified a place where the recursion would stop. Let's kill two birds with one stone.

code:
# let rec read_all_lines file_handle =
    try
      let line = input_line file_handle in
        line :: read_all_lines file_handle
    with
      End_of_file -> [];;
val read_all_lines : in_channel -> string list = <fun>
# let file_handle = open_in "test.ml";;
val file_handle : in_channel = <abstr>
# read_all_lines file_handle;;
- : string list =
["open Graphics;;"; ""; "let count = ref 0;;"; "";
 "open_graph \" 640x480\";;"; ""; "while true do";
 "   if button_down () then"; "      let (x, y) = mouse_pos () in";
 "      let coords = string_of_int x ^ \"x\" ^ string_of_int y in";
 "         begin"; "            moveto x y;";
 "            draw_string coords"; "         end;";
 "      count := !count + 1"; "done"]
wtd




PostPosted: Tue Nov 01, 2005 11:18 pm   Post subject: (No subject)

Modules

The fundamental means of code re-use is the module.

The creation of modules is quite easy in O'Caml.

A simple module:

code:
# module Foo =
    struct
      let bar = "baz"
    end;;
module Foo : sig val bar : string end


We can then easily use the bindings from the module.

code:
# print_endline Foo.bar;;
baz
- : unit = ()


Of course, it's possible to eliminate the need for the module name prefixed.

code:
# include Foo;;
val bar : string = "baz"
# bar;;
- : string = "baz"


We can create shortened names for modules.

code:
# module F = Foo;;
module F : sig val bar : string end
# F.bar;;
- : string = "baz"


Modules can be nested.

code:
- : string = "baz"
# module F =
    struct
      module G =
        struct
          let h = "i"
        end
    end;;
module F : sig module G : sig val h : string end end
# F.G.h;;
- : string = "i"


Of course, it's possible to control what is exported from a module.

code:
# module A : sig
    val b : unit -> unit
  end = struct
    let c = 42
    let b () = Printf.printf "%d\n" c
  end;;
module A : sig val b : unit -> unit end
# A.b ();;
42
- : unit = ()
# A.c;;
Characters 0-3:
  A.c;;
  ^^^
Unbound value A.c
wtd




PostPosted: Fri Nov 04, 2005 3:02 pm   Post subject: (No subject)

Objects

Yes, O'Caml permits object-oriented programming. Smile

So let's look at the simplest possible object.

code:
# object end;;
- : <  > = <obj>


Of course, that does nothing. It doesn't even create a class. It's an object that stands on its own with no class.

Let's create a class.

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


And we'll want to be able to create a new name object.

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


Of course, we'll want to pass in some parameters when we create the object: a first and last name.

code:
# class name initial_first_name initial_last_name =
    object
    end;;
class name : 'a -> 'b -> object  end
# let foo = new name "Bob" "Smith";;
val foo : name = <obj>


We'll probably want to store those values inside the object.

code:
# class name initial_first_name initial_last_name =
    object
      val first = initial_first_name
      val last  = initial_last_name
    end;;
class name : 'a -> 'b -> object val first : 'a val last : 'b end


Well, we still can't do anything with this. Those values are not accessible from outside the object. Let's provide accessor methods.

code:
# class name initial_first_name initial_last_name =
    object
      val first = initial_first_name
      val last  = initial_last_name
      method get_first_name = first
      method get_last_name  = last
    end;;
Characters 5-200:
  ..... name initial_first_name initial_last_name =
    object
      val first = initial_first_name
      val last  = initial_last_name
      method get_first_name = first
      method get_last_name  = last
    end..
Some type variables are unbound in this type:
  class name :
    'a ->
    'b ->
    object
      val first : 'a
      val last : 'b
      method get_first_name : 'a
      method get_last_name : 'b
    end
The method get_first_name has type 'a where 'a is unbound


The problem here is that O'Caml uses type inferencing to figure out what type everything should be, but it needs to see how something is being used to figure that out. We haven't provided enough use.

Let's give it more to think about. Let's create a method which formats the first and last name into a full name.

code:
# class name initial_first_name initial_last_name =
    object
      val first = initial_first_name
      val last  = initial_last_name
      method get_first_name = first
      method get_last_name  = last
      method get_full_name  = first ^ " " ^ last
    end;;
class name :
  string ->
  string ->
  object
    val first : string
    val last : string
    method get_first_name : string
    method get_full_name : string
    method get_last_name : string
  end


Now we can actually do something with this object.

code:
# let foo = new name "Bob" "Smith" in
    print_endline foo#get_full_name;;
Bob Smith
- : unit = ()
MysticVegeta




PostPosted: Fri Nov 04, 2005 6:20 pm   Post subject: (No subject)

How fast do you type? Confused
Cervantes




PostPosted: Fri Nov 04, 2005 7:15 pm   Post subject: (No subject)

I'm at variant types. Razz

Thanks for all of this, wtd. You truly are amazing.
wtd




PostPosted: Fri Nov 04, 2005 7:44 pm   Post subject: (No subject)

MysticVegeta wrote:
How fast do you type? Confused


Sadly, not very fast.
wtd




PostPosted: Sat Nov 05, 2005 2:00 am   Post subject: (No subject)

The option type

Varant types, when combined with generics make something really special possible.

Let's look at a task. I want to search a list for a value, and return the index of that value. Pretty straighforward, right?

What if you don't find it? Raise an exception? Well, that's pretty expensive and causes a lot of trouble. We could check to see if the list has the value first, but that means iterating over the list multiple times if it is there.

But... what if we could return a value that indicates "nothing"? Zilch, nada... squat. Or... the right value.

We can. The option type, with its None and Some constructors allows just that.

code:
# let rec find value lst index =
    match lst with
       [] -> None
     | x::xs ->
         if x = value then Some index
         else find value xs (index + 1);;
val find : 'a -> 'a list -> int -> int option = <fun>


code:
# find 3 [4;3;6;5] 0;;
- : int option = Some 1
# find 3 [4;1;6;5] 0;;
- : int option = None


We can then pattern match the return and take different courses of action based on whether it's Some something, or None.

code:
# match find 3 [4;3;5;6] 0 with
     None -> print_endline "Didn't find it."
   | Some index -> print_endline ("Found it at: " ^ string_of_int index);;
Found it at: 1
- : unit = ()
# match find 3 [4;1;5;6] 0 with
     None -> print_endline "Didn't find it."
   | Some index -> print_endline ("Found it at: " ^ string_of_int index);;
Didn't find it.
- : unit = ()
Display posts from previous:   
   Index -> Programming, General Programming -> Functional Programming
View previous topic Tell A FriendPrintable versionDownload TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

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


Style:  
Search: