28 February 2009

Ocaml: using Big_int, operator overloading and functors for dummies

I'm working on a functional programming project. I have encountered a few difficulties, so I will post here a few things that took me quite a while (and a lot of luck) to find. Especially operator overloading, where Google lead me only to endless discussions about wether it should exist or not and why it doesn't, when it actually does.

I will write a code snippet that will allow us to use integers and Big_ints the exact same way, for instance doubling them using one function named "twice". (In other words, putting two and two together with 4 modules and a functor - can something be more fun?)

First, let's get to the Big_ints:


    1 #load "nums.cma";;


    2 open Big_int;;




So far so good. Now, we'll declare two modules that can add numbers, one for integers, one for the Big_ints, but first, they'll need a signature:

    4 module type Add_sig =


    5   sig


    6     type t;;


    7     val (+): t -> t -> t


    8   end;;




Now, the modules. Notice how cool functional programming is, you can assign functions like other values:

   10 module Int_modl=


   11   struct


   12    type t = int


   13    let (+) = (+);;


   14  end;;


   15 


   16 module BigInt_modl =


   17   struct


   18     type t = big_int


   19     let (+) = add_big_int;;


   20  end;;



The "let (+) = (+)" looks stupid, but is required.

Now we'll write a functor to transform these two modules in two new modules that have a richer functionality (okay, "twice" is not "rich", but that's just an example and I hope you can see the big picture), that we will implement only once, though int and Big_int are two completely different data types and have completely different additions ("+" and "add_big_int") - again, notice how cool functional programming is.

   22 module Add =


   23  functor (Modl : Add_sig) ->


   24   struct


   25     type t = Modl.t


   26     let (+) = Modl.(+)


   27     let twice t = t + t;;


   28   end;;




Nice functor. Let's use it to create the previously mentioned modules and we're almost there:

   30 module Ints = Add(Int_modl);;


   31 module Bigs = Add(BigInt_modl);;




Okay, time to see if that works:

33 Ints.twice 2;;


34 int_of_big_int (Bigs.twice (big_int_of_int 2));;



We have a bit of hassle with creating and printing the Big_ints (thus the longer code), but in the end, we defined one function "twice" that uses the same operator "+" to perform technically completely different operations (with an emphasis on technically - you could use this technique to make "+" mean subtraction or multiplication for the integers and that would be nasty).

Finally, the entire code, ready to copy, paste and... run!

#load "nums.cma";;


open Big_int;;


 


module type Add_sig =


  sig


    type t;;


    val (+): t -> t -> t


  end;;


 


module Int_modl=


  struct


   type t = int


   let (+) = (+);;


 end;;


 


module BigInt_modl =


  struct


    type t = big_int


    let (+) = add_big_int;;


 end;;


 


 module Add =


 functor (Modl : Add_sig) ->


  struct


    type t = Modl.t


    let (+) = Modl.(+)


    let twice t = t + t;;


  end;;


 


module Ints = Add(Int_modl);;


module Bigs = Add(BigInt_modl);;


 


Ints.twice 2;;


int_of_big_int (Bigs.twice (big_int_of_int 2));;


No comments:

Post a Comment