English version
Accueil     À propos     Téléchargement     Ressources     Contactez-nous    

Ce site est rarement mis à jour. Pour les informations les plus récentes, rendez-vous sur le nouveau site OCaml à l'adresse ocaml.org.

Browse thread
Faking multimethods in OCaml
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: 2005-08-24 (19:25)
From: Yaron Minsky <yminsky@g...>
Subject: Faking multimethods in OCaml
I'm looking at ways of simulating multimethods in OCaml, and wanted to see 
if other people had any better solutions.

Here's a basic example. Imagine that we have two roles to be filled: let's 
call them a foo and a bar. We're going to have a few functions that opreate 
on foo's and bar's, and some of these functions operate jointly. Imagine, 
for example, that there is a function for adding new information (in the 
form of a float) to a foo as well as to a bar, and then there's a function 
for jointly evaluating a foo and a bar, which produces another float. Here's 
a simple example of how this might be realized. First, we might have two 
base modules, each implementing.

module M1
type foo = ...
type bar = ....

let update_foo foo (x:float) = ...
val update_bar bar (x:float) = ..
val eval foo bar : float = ...

module M2
type foo = ...
type bar = ....

let update_foo foo (x:float) = ...
val update_bar bar (x:float) = ..
val eval foo bar : float = ...

Now let's say I want to create a module that combines these two modules into 
one. It would support all the same functions, but in each slot it could 
either take one of types from M1 or from M2. The multi-method issue comes in 
when you're trying to apply the eval function to a foo and a bar. Here, we 
want the function to throw an exception if the foo and the bar don't come 
from the same module. We can achieve this as follows:

module Joined 
type foo = [ `M1 of M1.foo | `M2 of M2.foo ]
type bar = [ `M1 of M1.bar | `M2 of M2.bar ]

let update_foo foo x = match foo with
| `M1 foo -> M1.update_foo foo x
| `M2 foo -> M2.update_foo foo x

let update_bar bar x = match bar with
| `M1 foo -> M1.update_foo foo x
| `M2 foo -> M2.update_foo foo x

let eval foo bar = match foo,bar with
| `M1 foo, `M1 bar -> M1.eval foo bar
| `M2 foo, `M2 bar -> M2.eval foo bar
| _ -> failwith "mismatched foo and bar"

This basically does everything I want, but is rather a pain in practice. In 
particular, I have to redo this every time I want to combine a different set 
of modules. And in realistic examples, the number of functions and the 
number of modules to be combined gets quite a bit longer.

I can kind of do something like this in a more automatic way. I can kind of 
do this in the following way. I can wrap the original functions in the 
following way:

module M1
let wrapped_eval = function (foo,bar) -> 
`M1 foo, `M1 bar -> eval foo bar
| _ -> raise Exit

then, we can combine a list of such functions with a merge function, like 

let rec merge flist x = match flist with
[] -> None
| hd::tl -> 
match (try Some (hd x)
with Exit -> None)
| None -> merge tl x
| Some _ as rval -> rval

Now, I can define a merged eval function like this:

let m1m2_eval foo bar = merge [M1.wrapped_eval; M2.wrapped_eval] (foo,bar)

This basically works, but a fair amount of extra gymnastics is required to 
get this working cleanly, and the end result isn't all that efficient. So, I 
guess the question is, does anyone know of any better way of implementing 
multi-methods than this?