caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] classes vs modules
@ 2001-05-27 13:31 Tom _
  2001-05-27 16:14 ` Markus Mottl
  0 siblings, 1 reply; 8+ messages in thread
From: Tom _ @ 2001-05-27 13:31 UTC (permalink / raw)
  To: caml-list

Sorry if this is a FAQ, but it doesn't seem
listed.

I have been converting some SML code to OCAML, and
came across the following issue when converting a
Treap (randomized binary tree) data structure (in
fact, the analogous issue exists in SML).

The obvious module definition for a Treap is a
module like

    (* module-style implementation *)
    module Treap (Elt: ORDERED) =
      struct
        let rec insert e t = ...
        ...
      end
    module IntTreap =
	Treap(struct type t = int
              let lt (x:int) y = x<y end)

However, I can represent the same datatype as a
class as follows

    (* object-style implementation *)
    type 'a treap = Empty | Node of 'a treenode and
...
    class ['a] treapobj (lt: 'a -> 'a -> bool) =
      object
        method insert e (t:'a treap) = ...
        ...
       end
    let int_lt (x:int) y = x<y
    let inttreap = new treapobj int_lt
    inttreap#insert ...

This is, of course, roughly the equivalent of:

    (* closure-style implementation *)
    type 'a treap = Empty | Node of 'a treenode 
	and ...
    type 'a treapops = 
	{insert:'a -> 'a treap -> 'a treap; ...}
    let make_treap_ops (lt: 'a -> 'a -> bool) =
	let insert (e:'a) (t:'a treap) = ... in ...
	{ insert=insert; ... }
    let int_lt (x:int) y = x<y
    let inttreap = make_treap_ops int_lt
    inttreap.insert ...

Now, what are the differences between the two
approaches?

 -- In principle, with the module-based 
    implementation, IntTreap could 
    be optimized by inlining the
    "lt" function.  The compiler can statically
    determine the complete set of applications of
    the Treap functor that will occur in a
    program.  However, good ML implementations
    deliberately avoid taking advantage of this
    because they want to avoid code bloat.

 -- treapobj can be converted into a module without
    code duplication and without additional
    overhead:

    module TreapFromTreapObj (Elt: ORDERED) =
      struct
        let tobj = new treapobj Elt.lt
        let insert e t = tobj#insert
	...
      end

 -- treapobj is considerably more useful and flexible
    because the comparison function can be
    computed dynamically; this means that the
    compiler cannot statically determine all the
    comparison functions that treapobj might be
    used with, but as noted above, ML compilers
    avoid taking advantage of that knowledge
    anyway.

 -- Note that the "object-style" implementation is
    not the same as an imperative implementation
    you might find in a language like Java: the
    data structure itself and all its operations
    are purely functional.  The object merely
    holds the parameterization of the functions
    involved.

Now, the issue is that I can't find a way of
converting "module Treap" into the equivalent of a
"treapobj" without introducing considerable
overhead.  It seems any way of trying to do the
conversion involves wrapping up every element in a
class object.  This seems to limit somewhat the
reuse and functionality I can get out of existing
OCaml libraries written in a module style.

So, my question is: am I overlooking something?
Is there some way of getting the generality of the
object-based implementation out of pre-existing
module-based code without incurring significant
overhead?

If there isn't, wouldn't it make more sense to
write more datatypes in an object or closure style
and base module-style implementations on those?

It seems that the object-style approach is
somewhat like a limited form of first-class
modules.  Is there any more information about the
relationship between these approaches?

Thanks,
Thomas.

__________________________________________________
Do You Yahoo!?
Yahoo! Auctions - buy the things you want at great prices
http://auctions.yahoo.com/
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 13:31 [Caml-list] classes vs modules Tom _
@ 2001-05-27 16:14 ` Markus Mottl
  2001-05-27 20:54   ` Brian Rogoff
  2001-05-28  8:24   ` Andreas Rossberg
  0 siblings, 2 replies; 8+ messages in thread
From: Markus Mottl @ 2001-05-27 16:14 UTC (permalink / raw)
  To: Tom _; +Cc: caml-list

On Sun, 27 May 2001, Tom _ wrote:
>  -- treapobj is considerably more useful and flexible
>     because the comparison function can be
>     computed dynamically; this means that the
>     compiler cannot statically determine all the
>     comparison functions that treapobj might be
>     used with, but as noted above, ML compilers
>     avoid taking advantage of that knowledge
>     anyway.

You can also make use of "dynamic" comparison functions using local
modules, e.g.:

  let f lt1 lt2 =
     let module Int1Treap = Treap (struct let lt = lt1 end) in
     let module Int2Treap = Treap (struct let lt = lt2 end) in
     ...

> So, my question is: am I overlooking something?
> Is there some way of getting the generality of the
> object-based implementation out of pre-existing
> module-based code without incurring significant
> overhead?

Well, it depends on what you mean with "overhead". OCaml may spend ages
generating closures depending on the particular case of local modules.

This, however, is a one-time effort (per call to enclosing function):
later calls to module functions do not cost any additional overhead. If
such modules are heavily used after their creation, you'll hardly
notice any difference to "statically" (misleading - see below) created
modules. With objects, you'll have a noticable overhead for every method
call (and for parameterization anyway).

> It seems that the object-style approach is somewhat like a limited
> form of first-class modules.  Is there any more information about the
> relationship between these approaches?

There is still a distinction between modules that are parameterized at
runtime and modules that can be passed to and from functions. In fact,
closures arising from functor applications are _always_ computed at
runtime even at the toplevel, because this could cause side effects
like I/O (though the latter can be considered bad practice). In the
toplevel case these closures are only generated once and for all, but
people usually don't notice the startup overhead of their programs.

In short: your object-style approach is not really so much different from
the local module approach with the exception that object method lookups
come with the usual overhead. The only bonus for objects is that they can
reside within a polymorphic function, staying polymorphic themselves. I
am not sure whether this is really such a useful thing to have.

Out of curiousity, would it be unsound to allow functions like the
following:

  let f (cmp : 'a -> 'a -> bool) =
    let module SomeSet =
      Set.Make (struct type t = 'a let compare = cmp end) in
    ()

The compiler complains with:

  File "foo.ml", line 2, characters 49-51:
  Unbound type parameter 'a

This, however, is not true (in any case): 'a is bound by the explicit
function annotation. I am currently too lazy trying to find an example
where the type system could get a hole when the upper function were
allowed. Is there any?

Regards,
Markus Mottl

P.S.:  Please correct me if I have misexplained things...

-- 
Markus Mottl, mottl@miss.wu-wien.ac.at, http://miss.wu-wien.ac.at/~mottl
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 16:14 ` Markus Mottl
@ 2001-05-27 20:54   ` Brian Rogoff
  2001-05-27 23:13     ` Markus Mottl
  2001-05-27 23:52     ` Tom _
  2001-05-28  8:24   ` Andreas Rossberg
  1 sibling, 2 replies; 8+ messages in thread
From: Brian Rogoff @ 2001-05-27 20:54 UTC (permalink / raw)
  To: Markus Mottl; +Cc: Tom _, caml-list

On Sun, 27 May 2001, Markus Mottl wrote:
> Out of curiousity, would it be unsound to allow functions like the
> following:
> 
>   let f (cmp : 'a -> 'a -> bool) =
>     let module SomeSet =
>       Set.Make (struct type t = 'a let compare = cmp end) in
>     ()

You can do this now if you make a PolySet module by copying the
"monomorphic" implementation from the stdlib, changing elt and t to 
'a elt and 'a t and, using that instead of Set, something like:

let f (cmp : 'a -> 'a -> bool) =
  let module SomeSet =
    PolySet.Make 
      (struct type 'a t = 'a let compare = Pervasives.compare end) in
  ()

I use something very much like that in a few places, for example a
"uniquify" function on lists which eliminates duplicates.

I ended up creating polymorphic modules for a few of the stdlib modules on 
account of that old issue about functor instantiations recursive with a
type definition, so as to perform the ugly parameterization hack to
sidestep this ML limitation, but they are useful elsewhere too.

> The compiler complains with:
> 
>   File "foo.ml", line 2, characters 49-51:
>   Unbound type parameter 'a
> 
> This, however, is not true (in any case): 'a is bound by the explicit
> function annotation.

I thought you could never have type variables on the right hand side if
they were'nt also on the left? Besides, currently ML type annotations are 
without teeth, as you are well aware; as a translator of Okasaki's
algorithms you surely miss polymorphic recursion.

-- Brian


-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 20:54   ` Brian Rogoff
@ 2001-05-27 23:13     ` Markus Mottl
  2001-05-27 23:52     ` Tom _
  1 sibling, 0 replies; 8+ messages in thread
From: Markus Mottl @ 2001-05-27 23:13 UTC (permalink / raw)
  To: Brian Rogoff; +Cc: Tom _, caml-list

On Sun, 27 May 2001, Brian Rogoff wrote:
> On Sun, 27 May 2001, Markus Mottl wrote:
> > Out of curiousity, would it be unsound to allow functions like the
> > following:
> > 
> >   let f (cmp : 'a -> 'a -> bool) =
> >     let module SomeSet =
> >       Set.Make (struct type t = 'a let compare = cmp end) in
> >     ()
> 
> You can do this now if you make a PolySet module by copying the
> "monomorphic" implementation from the stdlib, changing elt and t to 
> 'a elt and 'a t and, using that instead of Set, something like:

One could certainly do this. Still, I am not sure whether this issue
isn't slightly different here: 'a has to be bound, of course, and
parameterized types let you create such bindings, making them explicit
across interfaces. If the binding didn't exist, you could accidently
mix sets with incompatible elements.

Here, however, the type variable is indeed bound, and there is no way
that we can escape this binding (if I am not mistaken): if you happen
to use set elements like e.g. integers somewhere in this function, all
"'a"s there would unify to this type. This makes it impossible that we
can accidently mix incompatible instances of sets, because there is only
one (since explicitly bound) version of "'a".

If we don't restrict the type of "'a" in the function body by using
values of this type in specific contexts, it seems we can't do evil things
either. Note that you cannot return values from functions if the type of
these values is defined in a local module only (again, because the type
couldn't be identified outside anymore). But returning values of types
bound outside (also ones of type 'a) should be perfectly ok, n'est-ce pas?

> I thought you could never have type variables on the right hand side if
> they were'nt also on the left?

Well, the requirement is that the type variable be bound so as not to
lose track about its true identity. I am not an expert in type systems
so maybe I just don't see other problems. At least it seems to me that
a relaxation (generalization?) of the typing rules here would work
fine. In any case, the current error message is obviously misleading,
because 'a is really bound.

> Besides, currently ML type annotations are without teeth, as you are
> well aware; as a translator of Okasaki's algorithms you surely miss
> polymorphic recursion.

Sure, but as I said, I think your argument concerning mandatory type
declarations is a different issue.

Regards,
Markus Mottl

-- 
Markus Mottl, mottl@miss.wu-wien.ac.at, http://miss.wu-wien.ac.at/~mottl
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 20:54   ` Brian Rogoff
  2001-05-27 23:13     ` Markus Mottl
@ 2001-05-27 23:52     ` Tom _
  2001-05-28  4:15       ` Brian Rogoff
  2001-05-28  8:34       ` Andreas Rossberg
  1 sibling, 2 replies; 8+ messages in thread
From: Tom _ @ 2001-05-27 23:52 UTC (permalink / raw)
  To: caml-list; +Cc: Brian Rogoff

> You can do this now if you make a PolySet module by
> copying the
> "monomorphic" implementation from the stdlib,
> changing elt and t to 
> 'a elt and 'a t and, using that instead of Set,
> something like:
> 
> let f (cmp : 'a -> 'a -> bool) =
>   let module SomeSet =
>     PolySet.Make 
>       (struct type 'a t = 'a let compare =
> Pervasives.compare end) in
>   ()

Thanks; that's very useful to know, and it seems
to be working.  I think it might be useful to feature
the ability to have polymorphic types in modules
a little more prominently in the documentation.
Maybe some of the standard modules could take
advantage of this more (it would require some
changes to their interfaces, I suppose).

The type signatures I get out of my module after
converting it are a little odd looking:

    type 'a t = 'a
    type 'a tree = Empty | Node of ...
    val insert :
	('a t t t -> 'a t t -> bool) -> 
	'a t t t -> 'a t tree -> 'a tree
    ...

Is there any useful information in the different
number of t's?  They should all be equivalent, right?

BTW, I opted for just passing the "lt" function
as explicit arguments to all the functions in the
module that need them.  That seems like the most
general approach, although it means some extra
arguments and it doesn't guarantee consistency,
as people might pass incompatible versions of "lt"
to different calls; a more convenient interface can
then be wrapped around that, maybe in a different
module

Cheers,
Tom.


__________________________________________________
Do You Yahoo!?
Yahoo! Auctions - buy the things you want at great prices
http://auctions.yahoo.com/
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 23:52     ` Tom _
@ 2001-05-28  4:15       ` Brian Rogoff
  2001-05-28  8:34       ` Andreas Rossberg
  1 sibling, 0 replies; 8+ messages in thread
From: Brian Rogoff @ 2001-05-28  4:15 UTC (permalink / raw)
  To: Tom _; +Cc: caml-list

On Sun, 27 May 2001, Tom _ wrote:

> > You can do this now if you make a PolySet module by
> > copying the
> > "monomorphic" implementation from the stdlib,
> > changing elt and t to 
> > 'a elt and 'a t and, using that instead of Set,
> > something like:
> > 
> > let f (cmp : 'a -> 'a -> bool) =
> >   let module SomeSet =
> >     PolySet.Make 
> >       (struct type 'a t = 'a let compare =
> > Pervasives.compare end) in
> >   ()
> 
> Thanks; that's very useful to know, and it seems
> to be working.  I think it might be useful to feature
> the ability to have polymorphic types in modules
> a little more prominently in the documentation.

Yes, or you have to make a habit of reading lots of the posts from
yesteryear in the mailing list archive. There is a lot of good information
there, unfortunately it isn't so easy to find. 

> Maybe some of the standard modules could take
> advantage of this more (it would require some
> changes to their interfaces, I suppose).

Yes, the interfaces would have to change. I think it is better that the 
interfaces don't have type variables; really the most compelling (IMO of
course!) reason for making them polymorphic is to use the parameterization 
trick for recursive types, and I am hoping that a future OCaml (OCaml 4?) 
handles this nicely by stealing Moscow ML features or by adopting the
mixin modules described briefly here by Tom Hirschowitz a few months ago.

> The type signatures I get out of my module after
> converting it are a little odd looking:
> 
>     type 'a t = 'a
>     type 'a tree = Empty | Node of ...
>     val insert :
> 	('a t t t -> 'a t t -> bool) -> 
> 	'a t t t -> 'a t tree -> 'a tree
>     ...
> 
> Is there any useful information in the different
> number of t's?  They should all be equivalent, right?

Something looks wrong to me here. Could you post more of your code, or
provide a pointer to the original SML code? I don't see why you have a 
'a t t t. The OCaml code should look a lot like the SML code, and the
signatures should only differ a little.

> BTW, I opted for just passing the "lt" function
> as explicit arguments to all the functions in the
> module that need them. 

Why not just parameterize the module by the comparison function? 

> That seems like the most general approach, although it means some extra
> arguments and it doesn't guarantee consistency,
> as people might pass incompatible versions of "lt"
> to different calls;

Right, I think you should pull the comaprison out of the functions and 
parameterize the module for this reason. 

-- Brian


-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 16:14 ` Markus Mottl
  2001-05-27 20:54   ` Brian Rogoff
@ 2001-05-28  8:24   ` Andreas Rossberg
  1 sibling, 0 replies; 8+ messages in thread
From: Andreas Rossberg @ 2001-05-28  8:24 UTC (permalink / raw)
  To: caml-list

Markus Mottl wrote:
> 
> Out of curiousity, would it be unsound to allow functions like the
> following:
> 
>   let f (cmp : 'a -> 'a -> bool) =
>     let module SomeSet =
>       Set.Make (struct type t = 'a let compare = cmp end) in
>     ()

It would be sound. Actually, Moscow ML allows it as an extension to SML.
(Though I am not completely sure whether OCaml's odd semantics of type
variables in annotations makes a difference. But I do not see how it
should.)

	- Andreas

-- 
Andreas Rossberg, rossberg@ps.uni-sb.de

"Computer games don't affect kids.
 If Pac Man affected us as kids, we would all be running around in
 darkened rooms, munching pills, and listening to repetitive music."
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

* Re: [Caml-list] classes vs modules
  2001-05-27 23:52     ` Tom _
  2001-05-28  4:15       ` Brian Rogoff
@ 2001-05-28  8:34       ` Andreas Rossberg
  1 sibling, 0 replies; 8+ messages in thread
From: Andreas Rossberg @ 2001-05-28  8:34 UTC (permalink / raw)
  To: caml-list; +Cc: Tom _

Tom _ wrote:
> 
> BTW, I opted for just passing the "lt" function
> as explicit arguments to all the functions in the
> module that need them.  That seems like the most
> general approach, although it means some extra
> arguments and it doesn't guarantee consistency,
> as people might pass incompatible versions of "lt"
> to different calls;

You can avoid this by passing it to the creator
function only and store it inside the object:

	val make_treap : ('a -> 'a -> bool) -> 'a treap

The treap type would look like this:

	type 'a treap' = Empty | ...
	type 'a treap  = {treap' : treap'; lt : 'a -> 'a -> bool}

In most cases I would prefer the functor version, though.

Hope this helps,

	- Andreas

-- 
Andreas Rossberg, rossberg@ps.uni-sb.de

"Computer games don't affect kids.
 If Pac Man affected us as kids, we would all be running around in
 darkened rooms, munching pills, and listening to repetitive music."
-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr


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

end of thread, other threads:[~2001-05-28  8:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-05-27 13:31 [Caml-list] classes vs modules Tom _
2001-05-27 16:14 ` Markus Mottl
2001-05-27 20:54   ` Brian Rogoff
2001-05-27 23:13     ` Markus Mottl
2001-05-27 23:52     ` Tom _
2001-05-28  4:15       ` Brian Rogoff
2001-05-28  8:34       ` Andreas Rossberg
2001-05-28  8:24   ` Andreas Rossberg

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).