caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* objects: the lack of method overloading (looking for a way around it)
@ 2010-10-04 17:32 philippe
  2010-10-05 15:37 ` philippe
  0 siblings, 1 reply; 2+ messages in thread
From: philippe @ 2010-10-04 17:32 UTC (permalink / raw)
  To: caml-list

Hello,

I'm just beginning a real, usefull usage of the object system in ocaml.
I'm a bit stalled on one thing, it's the lack of method overloading like in C++.
My need is in a case of building a rather simple pipeline pattern:
each instance of an element (a node) in the pipeline can get a
numerical value from the previous node, do some computation on it, and
store it locally in a hash for caching/efficiency, as returning it
using the #get method.

The real need see most of the time a 1D array of float (a vector) in
input and output of each node.
but sometime a Complex.t array.

So I need some kind of polymorphism for allowing :

float array -> float array
Complet.t array -> float array
float array -> Complex.t array
Complex.t array -> Complex.t array

Here's a shorter (a bit dumb) demo code using int -> int.

(I would much prefer avoid to store float in the real part of a
complex array - in the real (genuine) code the array may be long,
100'000 elements and more, it's code a bit sensitive about
performances).

Look to the comment MY QUESTION in the definition of the process
method in the class node_plus

THANK YOU !

--
Philippe Strauss

--- 8< ---

open Common
open Log

(* the real code has more usefull type here *)
type node_parameters = Plus1 | Plus2

(* a class for storing parameters usefull for the whole pipeline chain *)
class parameters =
    object(self)
        (* useless here - usefull in the real code *)
        val mutable sf = 44100
        (* a value we will add in a demo node *)
        val mutable add = 1
        method get_sf = sf
        method set_sf sfreq =
            sf <- sfreq ;
        method get_add = add
        method set_add ntoadd =
            add <- ntoadd
        (* this is for the cache part of each node: we compute a hash
of parameter to distinguish hash entries btwn parameters change *)
        method get_parh np (node_id: int) =
            let md5 s = Digest.to_hex (Digest.string s) in
            match np with
            (* the real code doesn't use always (sf, add) but
parameter influing the processing of a node *)
            | Plus1 -> md5 (Marshal.to_string (sf, add) [Marshal.No_sharing])
            | Plus2 -> md5 (Marshal.to_string (sf, add) [Marshal.No_sharing])
end

(* main code of a node: a boileplate around a hash to cache computed value *)
class virtual node_virt prev parm hn =
    object(self)
        (* instance of a previous node in the pipeline *)
        val previous = prev
        (* instance of the parameters *)
        val parms = parm
        (* the hash for caching *)
        val hres = Hashtbl.create hn
        (* a serial, bumped up plus one on each cache miss *)
        val mutable id = 0
        method get_id = id
        method inc_id =
            id <- id + 1
        (* for passing to (parms #get_parh (self #ptyp ())) in the
real code, when adding a result into the hash *)
        method virtual ptyp: unit -> node_parameters
        (* the real computation is defined in a non virtual method
later, as simply as possible *)
        method virtual process: int -> int
        (* process and store: call process, then store in cache.
called on a cache miss *)
        method private pstore: (int * int) =
            let prid, data = prev #get in
            (* processing *)
            let new_data = self #process data in
            (* we're in a cache miss: bump the serial +1 *)
            self #inc_id ;
            let myid = self #get_id in
            Hashtbl.add hres (parms #get_parh Plus1 myid) new_data ;
            (myid, new_data)
        method get =
            try
                (* memoize id to avoid race cond *)
                let myid = self #get_id in
                (* here again in the real code, we lookup the hash
using (parms #get_parh (self #ptyp ()) myid) *)
                let data = Hashtbl.find hres (parms #get_parh Plus1 myid) in
                (myid, data)
            with Not_found -> Printf.printf "node_virt #get: cache
miss!\n%!"; self #pstore
end

(* a trivial node: add parm #get_add to the integer we receive from
the previous node *)
class node_plus prev parm hn =
    object(self)
        inherit node_virt prev parm hn
        method ptyp () = Plus1
        (*
            MY QUESTION: what is the best way to allow not only int ->
int for this process method,
            but also: int -> float, float -> int, float -> float ?
            The method minimizing the code written in this non virtual
node definition, since I may end with 15 or 30
            differents derived class of node_virt like this single one ?
        *)
        method process n =
            n + (parm #get_add)
end

(* a front of line, head node having a get method of the same type
than those derived from node_virt *)
class node_head parm =
    object(self)
        val parms = parm
        method get =
            (0, 1)
end


let () =
    let p = new parameters in
    p #set_sf 44100 ; (* useless indeed, just for demo *)
    p #set_add 2 ; (* usefull *)
    let n0 = new node_head p in
    let n1 = new node_plus n0 p 3 in
    Printf.printf "%d\n" (snd (n0 #get)) ;
    Printf.printf "%d\n" (snd (n1 #get)) ;
    Printf.printf "%d\n" (snd (n1 #get)) ;
    p #set_add 1 ;
    Printf.printf "%d\n" (snd (n1 #get))


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: objects: the lack of method overloading (looking for a way around it)
  2010-10-04 17:32 objects: the lack of method overloading (looking for a way around it) philippe
@ 2010-10-05 15:37 ` philippe
  0 siblings, 0 replies; 2+ messages in thread
From: philippe @ 2010-10-05 15:37 UTC (permalink / raw)
  To: caml-list

philippe,

use parametrized classes.

--
philippe

On Mon, Oct 4, 2010 at 7:32 PM, philippe <kelvin.bitnick@gmail.com> wrote:
> Hello,
>
> I'm just beginning a real, usefull usage of the object system in ocaml.
> I'm a bit stalled on one thing, it's the lack of method overloading like in C++.
> My need is in a case of building a rather simple pipeline pattern:
> each instance of an element (a node) in the pipeline can get a
> numerical value from the previous node, do some computation on it, and
> store it locally in a hash for caching/efficiency, as returning it
> using the #get method.
>
> The real need see most of the time a 1D array of float (a vector) in
> input and output of each node.
> but sometime a Complex.t array.
>
> So I need some kind of polymorphism for allowing :
>
> float array -> float array
> Complet.t array -> float array
> float array -> Complex.t array
> Complex.t array -> Complex.t array
>
> Here's a shorter (a bit dumb) demo code using int -> int.
>
> (I would much prefer avoid to store float in the real part of a
> complex array - in the real (genuine) code the array may be long,
> 100'000 elements and more, it's code a bit sensitive about
> performances).
>
> Look to the comment MY QUESTION in the definition of the process
> method in the class node_plus
>
> THANK YOU !
>
> --
> Philippe Strauss
>
> --- 8< ---
>
> open Common
> open Log
>
> (* the real code has more usefull type here *)
> type node_parameters = Plus1 | Plus2
>
> (* a class for storing parameters usefull for the whole pipeline chain *)
> class parameters =
>    object(self)
>        (* useless here - usefull in the real code *)
>        val mutable sf = 44100
>        (* a value we will add in a demo node *)
>        val mutable add = 1
>        method get_sf = sf
>        method set_sf sfreq =
>            sf <- sfreq ;
>        method get_add = add
>        method set_add ntoadd =
>            add <- ntoadd
>        (* this is for the cache part of each node: we compute a hash
> of parameter to distinguish hash entries btwn parameters change *)
>        method get_parh np (node_id: int) =
>            let md5 s = Digest.to_hex (Digest.string s) in
>            match np with
>            (* the real code doesn't use always (sf, add) but
> parameter influing the processing of a node *)
>            | Plus1 -> md5 (Marshal.to_string (sf, add) [Marshal.No_sharing])
>            | Plus2 -> md5 (Marshal.to_string (sf, add) [Marshal.No_sharing])
> end
>
> (* main code of a node: a boileplate around a hash to cache computed value *)
> class virtual node_virt prev parm hn =
>    object(self)
>        (* instance of a previous node in the pipeline *)
>        val previous = prev
>        (* instance of the parameters *)
>        val parms = parm
>        (* the hash for caching *)
>        val hres = Hashtbl.create hn
>        (* a serial, bumped up plus one on each cache miss *)
>        val mutable id = 0
>        method get_id = id
>        method inc_id =
>            id <- id + 1
>        (* for passing to (parms #get_parh (self #ptyp ())) in the
> real code, when adding a result into the hash *)
>        method virtual ptyp: unit -> node_parameters
>        (* the real computation is defined in a non virtual method
> later, as simply as possible *)
>        method virtual process: int -> int
>        (* process and store: call process, then store in cache.
> called on a cache miss *)
>        method private pstore: (int * int) =
>            let prid, data = prev #get in
>            (* processing *)
>            let new_data = self #process data in
>            (* we're in a cache miss: bump the serial +1 *)
>            self #inc_id ;
>            let myid = self #get_id in
>            Hashtbl.add hres (parms #get_parh Plus1 myid) new_data ;
>            (myid, new_data)
>        method get =
>            try
>                (* memoize id to avoid race cond *)
>                let myid = self #get_id in
>                (* here again in the real code, we lookup the hash
> using (parms #get_parh (self #ptyp ()) myid) *)
>                let data = Hashtbl.find hres (parms #get_parh Plus1 myid) in
>                (myid, data)
>            with Not_found -> Printf.printf "node_virt #get: cache
> miss!\n%!"; self #pstore
> end
>
> (* a trivial node: add parm #get_add to the integer we receive from
> the previous node *)
> class node_plus prev parm hn =
>    object(self)
>        inherit node_virt prev parm hn
>        method ptyp () = Plus1
>        (*
>            MY QUESTION: what is the best way to allow not only int ->
> int for this process method,
>            but also: int -> float, float -> int, float -> float ?
>            The method minimizing the code written in this non virtual
> node definition, since I may end with 15 or 30
>            differents derived class of node_virt like this single one ?
>        *)
>        method process n =
>            n + (parm #get_add)
> end
>
> (* a front of line, head node having a get method of the same type
> than those derived from node_virt *)
> class node_head parm =
>    object(self)
>        val parms = parm
>        method get =
>            (0, 1)
> end
>
>
> let () =
>    let p = new parameters in
>    p #set_sf 44100 ; (* useless indeed, just for demo *)
>    p #set_add 2 ; (* usefull *)
>    let n0 = new node_head p in
>    let n1 = new node_plus n0 p 3 in
>    Printf.printf "%d\n" (snd (n0 #get)) ;
>    Printf.printf "%d\n" (snd (n1 #get)) ;
>    Printf.printf "%d\n" (snd (n1 #get)) ;
>    p #set_add 1 ;
>    Printf.printf "%d\n" (snd (n1 #get))
>


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2010-10-05 15:37 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-04 17:32 objects: the lack of method overloading (looking for a way around it) philippe
2010-10-05 15:37 ` philippe

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).