Browse thread
Faking multimethods in OCaml
- Yaron Minsky
[
Home
]
[ Index:
by date
|
by threads
]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
| Date: | -- (:) |
| 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 struct type foo = ... type bar = .... let update_foo foo (x:float) = ... val update_bar bar (x:float) = .. val eval foo bar : float = ... end module M2 struct type foo = ... type bar = .... let update_foo foo (x:float) = ... val update_bar bar (x:float) = .. val eval foo bar : float = ... end 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 struct 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" end 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 struct ... let wrapped_eval = function (foo,bar) -> `M1 foo, `M1 bar -> eval foo bar | _ -> raise Exit end then, we can combine a list of such functions with a merge function, like this: let rec merge flist x = match flist with [] -> None | hd::tl -> match (try Some (hd x) with Exit -> None) with | 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? Yaron