Am Donnerstag, den 09.02.2017, 18:19 -0500 schrieb Steffen Smolka:Thanks for the detailed answer, Jeremy!If you're keen to stick with objectsYes, I rely on inheritance and dynamic dispatch for what I have in mind. (This is actually the first time I'm touching the dark object oriented side of OCaml :) )To give some more context, I am refactoring some code that uses modules and no objects. The reason I want to move to objects is that I want to derive a slightly enhanced module from some base implementation. Inheritance + dynamic dispatch allow me to do so with very little trouble: I can simply overwrite a few methods from the base implementation.I suppose I could achieve the same by turning the base module into a functor, and abstracting over the functions that my enhanced implementation needs to replace. I think it won't be quite as natural, but I'll give that a try.First-class modules could also be an option: Let's assume both the base module and the modified one can use the same module type:module T = sig ... endNow, define the base module likemodule Base : T =...endthen, define the modified one:module Mod : T =include Base... now override what you need to change but note that there's no dynamic dispatch ...endOf course, you could also use functors for making these modules.Now turn this into first-class modules and pass them around:let base = (module Base : T)let mod = (module Mod : T)The syntax for unpacking the module is quite cumbersome:let module M = (val base : T) inM.function ...Unfortunately, there's nothing simple like base.function.Compared with objects you get:
- You can also put types and (to some degree) modules into these "code containers"
- However, there's no dynamic dispatch except you arrange explicitly for that, e.g. with references to functions
- Generally, a heavier syntax, but it might be ok
GerdOr you could select the encoding using a variant type:Good idea, and I'm happy with the syntax for the caller. But I'm more concerned with the organization of the code; this would mix the Latin1 and Utf8 implementations. I would rather keep them separate.-- SteffenOn Thu, Feb 9, 2017 at 5:55 PM, Jeremy Yallop <yallop@gmail.com> wrote:Dear Steffen,
On 9 February 2017 at 20:36, Steffen Smolka <smolka@cs.cornell.edu> wrote:
> Is it possible to create namespaces inside an object? Concretely, I would
> like to write
>
> class buffer = object(self)
> ...
> method get = ...
>
> module Latin1 = struct
> method get = ...
> end
>
> module Utf8 = struct
> method get = ...
> end
> end
>
> so that given an object b : buffer, I can call methods
> b#get
> b#Latin1.get
> b#Utf8.get
It's possible to achieve something like this using methods that return
objects. If your nested objects don't need to access the internal
state of the parent then you might write it like this:
class buffer =
let latin1 = object
method get = ...
end
and utf8 = object
method get = ...
end in
object(self)
...
method get = ...
method latin1 = latin1
method utf8 = utf8
end
With this approach you can write
b#get
b#latin1#get
b#utf8#get
which, apart from some minor orthographic differences, looks like what
you were aiming for.
Your intuition that this isn't really idiomatic OCaml is right,
though. In OCaml, unlike some other languages with classes and
objects, classes are not usually used as namespaces; method names are
globally (or, rather, "ambiently") scoped, and there's no real support
for the kind of nesting that you're interested in. Instead, people
typically build nested namespaces using modules:
module Buffer =
struct
let get = ...
module Latin1 = struct
let get = ...
end
module Utf8 = struct
let get = ...
end
end
With the module approach you write the 'receiver' after the 'method'
rather than before, but that doesn't seem like a huge hardship. (10%
of the world manages to get by with VSO languages.)
Buffer.get b ...
Buffer.Latin1.get b ...
Buffer.Utf8.get b ...
If you're keen to stick with objects there are slightly more idiomatic
ways to make it work. You could, of course, replace the '.' with a
'_' and define methods 'latin1_get', 'utf8_get' in place of
'Latin1.get', 'Utf8.get'. Or you could select the encoding using a
variant type:
type enc = Latin1 | Utf8
class buffer =
object (self)
method get = function
| Latin1 -> ...
| Utf8 -> ...
end
Of course, the order of the words in an invocation changes again, but
there's no real increase in complexity for the caller:
b#get Latin1
b#get Utf8
This last approach can be taken quite far -- for example, you could
enrich the type 'enc' so that the return type of 'get' varies
according to the encoding.
Kind regards,
Jeremy
-- ------------------------------------------------------------ Gerd Stolpmann, Darmstadt, Germany gerd@gerd-stolpmann.de My OCaml site: http://www.camlcity.org Contact details: http://www.camlcity.org/ contact.html Company homepage: http://www.gerd-stolpmann.de ------------------------------------------------------------