caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* OO programming
@ 2008-02-21  9:31 Tiphaine Turpin
  2008-02-21  9:42 ` [Caml-list] " Erik de Castro Lopo
                   ` (3 more replies)
  0 siblings, 4 replies; 32+ messages in thread
From: Tiphaine Turpin @ 2008-02-21  9:31 UTC (permalink / raw)
  To: caml-list

Hello,

After a few unsuccessfull tries with using the object oriented features 
of ocaml, I have been looking for ways to write classes that have a 
chance to typecheck. The usual problem is that objects refer to other 
objects, those objects may refer back to objects referring to them, then 
objects refer to different objects, some of them having more methods 
than others (subtyping), etc. and finally the programmer has to give a 
type to all those beautifull mutable fields that are not fully 
specified, or make them parametric. Of course, the resulting classes 
should be reusable, that is, one should be able to extend a collection 
of related classes simultaneously, such that the fields now have the 
relevant subtype instead of the type they had before.

The best guidelines that I found are in the following course from Didier 
Remy :

http://caml.inria.fr/pub/docs/u3-ocaml/ocaml-objects.html#toc13

He uses parametric classes  (parametric in the type of the related 
objects) so that they can be extended. However I'm still unsatisfied 
with this solution, because the related classes are written 
independently, or, more precisely, their dependencies remain in the head 
of the programmer and have no concretization in the language. For 
example if a class refer to a method provided by a related class, this 
way of writing them does not guarantee that the method is actually 
defined. Only when creating and linking the objects together will the 
type-checker reject the program, if for example, the method has been 
misspelled in one class. So for me this coding scheme has the drawback 
that it unplugs the type-checker and just call it at the end. For me the 
"ideal" use of objects would use mutually recursive classes with fields 
defined with a type referring to the name of the other class. The 
problem is that simply using the closed type of the other classs often 
prevent any further refinement.

Hence my question: does anyone knows a way of combining the reusability 
of sets of related classes with a more modular (type/consistency)-checking ?

Tiphaine Turpin

P.S. : Sorry for not providing any example myself ; the above link has 
very clear ones, that express exactly what I mean.


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

* Re: [Caml-list] OO programming
  2008-02-21  9:31 OO programming Tiphaine Turpin
@ 2008-02-21  9:42 ` Erik de Castro Lopo
  2008-02-21 13:38 ` Remi Vanicat
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 32+ messages in thread
From: Erik de Castro Lopo @ 2008-02-21  9:42 UTC (permalink / raw)
  To: Tiphaine Turpin; +Cc: caml-list

Tiphaine Turpin wrote:

> After a few unsuccessfull tries with using the object oriented features 
> of ocaml, I have been looking for ways to write classes that have a 
> chance to typecheck.

OO in Ocaml is vastly different from OO in languages you might be used
to like Java, C++, Python etc.

If you are new to Ocaml, I highly recommend you forget about using Ocaml's
OO features until you feel comfortable with the base features of the 
language.

Erik
-- 
-----------------------------------------------------------------
Erik de Castro Lopo
-----------------------------------------------------------------
"Projects promoting programming in natural language are intrinsically
doomed to fail." -- Edsger Dijkstra


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

* Re: OO programming
  2008-02-21  9:31 OO programming Tiphaine Turpin
  2008-02-21  9:42 ` [Caml-list] " Erik de Castro Lopo
@ 2008-02-21 13:38 ` Remi Vanicat
  2008-02-24 16:33 ` [Caml-list] " Dirk Thierbach
  2008-02-26  6:17 ` Jacques Garrigue
  3 siblings, 0 replies; 32+ messages in thread
From: Remi Vanicat @ 2008-02-21 13:38 UTC (permalink / raw)
  To: caml-list

Tiphaine Turpin <Tiphaine.Turpin@irisa.fr> writes:

> Hello,
>
> After a few unsuccessfull tries with using the object oriented
> features of ocaml, I have been looking for ways to write classes that
> have a chance to typecheck. The usual problem is that objects refer to
> other objects, those objects may refer back to objects referring to
> them, then objects refer to different objects, some of them having
> more methods than others (subtyping), etc. and finally the programmer
> has to give a type to all those beautifull mutable fields that are not
> fully specified, or make them parametric. Of course, the resulting
> classes should be reusable, that is, one should be able to extend a
> collection of related classes simultaneously, such that the fields now
> have the relevant subtype instead of the type they had before.
>
> The best guidelines that I found are in the following course from
> Didier Remy :
>
> http://caml.inria.fr/pub/docs/u3-ocaml/ocaml-objects.html#toc13
>
> He uses parametric classes  (parametric in the type of the related
> objects) so that they can be extended. However I'm still unsatisfied
> with this solution, because the related classes are written
> independently, or, more precisely, their dependencies remain in the
> head of the programmer and have no concretization in the language. For
> example if a class refer to a method provided by a related class, this
> way of writing them does not guarantee that the method is actually
> defined. Only when creating and linking the objects together will the
> type-checker reject the program, if for example, the method has been
> misspelled in one class. So for me this coding scheme has the drawback
> that it unplugs the type-checker and just call it at the end. For me
> the "ideal" use of objects would use mutually recursive classes with
> fields defined with a type referring to the name of the other
> class. The problem is that simply using the closed type of the other
> classs often prevent any further refinement.
>
> Hence my question: does anyone knows a way of combining the
> reusability of sets of related classes with a more modular
> (type/consistency)-checking ?

something like that might work (from the Dider Remy example)


class ['observer] subject =
object (self : 'mytype)
  val mutable observers : 'observer list = []
  method add obs = observers <- obs :: observers
  method notify (message : 'observer -> 'mytype -> unit) =
    List.iter (fun obs -> message obs self) observers
end;;

class ['subject] observer =
object
  constraint 'subject = 'a #subject
end;;

Note that it doesn't solve completely the problem (as #subject is
still an open type) but it might catch some problem.

-- 
Rémi Vanicat


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

* Re: [Caml-list] OO programming
  2008-02-21  9:31 OO programming Tiphaine Turpin
  2008-02-21  9:42 ` [Caml-list] " Erik de Castro Lopo
  2008-02-21 13:38 ` Remi Vanicat
@ 2008-02-24 16:33 ` Dirk Thierbach
  2008-02-25  9:23   ` Tiphaine.Turpin
  2008-02-26  6:17 ` Jacques Garrigue
  3 siblings, 1 reply; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-24 16:33 UTC (permalink / raw)
  To: caml-list

On Thu, Feb 21, 2008 at 10:31:42AM +0100, Tiphaine Turpin wrote:
> After a few unsuccessfull tries with using the object oriented features 
> of ocaml, I have been looking for ways to write classes that have a 
> chance to typecheck. 

The way I handle this is the following:

> The usual problem is that objects refer to other objects, 

I avoid this as much as possible. Instead of "connecting" objects
to other objects, I "connect" an objects to "actions" (functions),
which may be closures that include other objects. The typical example
which has already been given is the observer-pattern, which I use
as follows:

class ['a] observer = 
object(self)
  val mutable actions = []
  method register action =
    actions <- action :: actions
  method notify (arg : 'a) = 
    List.iter (fun f -> f arg) actions
end;;

No class for subject needed. Many of the objects I use in the GUI layer 
just inherit from observer, and it's also handy if the action you want
doesn't refer to an explicit object in the OCaml layer (because the
state is kept in the external GUI layer)

If possible, I try to make each object as independently from the rest
of the world as I can (after all, grouping a related part of the world
state together is very central to OO), and then I "plug together" all
these objects at a higher level.

> those objects may refer back to objects referring to them, 

No problem with the above way of using "actions". 

> then objects refer to different objects, some of them having more
> methods than others (subtyping),

I also use objects as parameters, both for classes and functions.
That all works as it is intended, the type just collects any method
calls that are used in the parameter. The only disadvantage is that
a type error is then usually discovered only at the time when I "plug"
all these things together. That's somewhat annoying, and if I really
cannot figure out what's going on, introducing type declarations into
a few places usually helps.

> etc. and finally the programmer has to give a type to all those
> beautifull mutable fields that are not fully specified, or make them
> parametric.

That problem often goes away if these fields are initialized, or used
in a method (which can be annoying if the class is not yet completely
written, but you want to test the stuff you have so far).

And of course, I try to use as little mutable state as possible (after
all, it's a functional programming language :-)

Much more annoying is that one also has to give types to arguments in
methods that are unused (they are present make them compatible with
another class, which uses them; or they are required by the GUI
layer), and that OCaml has now way to seperate method/function
declaration and type declaration (unless one uses a mli file, which is
again inconvenient because it splits information that belongs together
into two complete different places). I'd really appreciate it if one
could just mix "val" type declcarations with "let" or "method" code
declarations.

> Of course, the resulting classes should be reusable, that is, one
> should be able to extend a collection of related classes
> simultaneously, such that the fields now have the relevant subtype
> instead of the type they had before.

Not sure what exactly you mean here. Could you give an example?

> The best guidelines that I found are in the following course from
> Didier Remy [...] For example if a class refer to a method provided
> by a related class, this way of writing them does not guarantee that
> the method is actually defined. Only when creating and linking the
> objects together will the type-checker reject the program, if for
> example, the method has been misspelled in one class. So for me this
> coding scheme has the drawback that it unplugs the type-checker and
> just call it at the end.

Yes, see above. You can use class types to get around this problem,
but if you keep the classes small, one can live with.

> For me the "ideal" use of objects would use mutually recursive
> classes with fields defined with a type referring to the name of the
> other class.

Ugh. No, thanks :-)

- Dirk


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

* Re: [Caml-list] OO programming
  2008-02-24 16:33 ` [Caml-list] " Dirk Thierbach
@ 2008-02-25  9:23   ` Tiphaine.Turpin
  2008-02-25 15:48     ` Edgar Friendly
  2008-02-25 20:10     ` Dirk Thierbach
  0 siblings, 2 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-25  9:23 UTC (permalink / raw)
  To: caml-list

Dirk Thierbach a écrit :
> On Thu, Feb 21, 2008 at 10:31:42AM +0100, Tiphaine Turpin wrote:
>   
>> The usual problem is that objects refer to other objects, 
>>     
>
> I avoid this as much as possible.
No connecting of objects, therefore no mutually recursive methods, (and 
many arguments added to every function to carry the missing context), 
little mutable state, and no use of class names to specify types... I'm 
sure that many problems go away in this setting, and there are still 
advantages to using objects (inheritance, etc.) but isn't this a 
somewhat degraded version of the object paradigm ?

>  Instead of "connecting" objects
> to other objects, I "connect" an objects to "actions" (functions),
> which may be closures that include other objects. The typical example
> which has already been given is the observer-pattern, which I use
> as follows:
>
> class ['a] observer = 
> object(self)
>   val mutable actions = []
>   method register action =
>     actions <- action :: actions
>   method notify (arg : 'a) = 
>     List.iter (fun f -> f arg) actions
> end;;
>
> No class for subject needed. Many of the objects I use in the GUI layer 
> just inherit from observer, and it's also handy if the action you want
> doesn't refer to an explicit object in the OCaml layer (because the
> state is kept in the external GUI layer)
> [...]
>   
> Much more annoying is that one also has to give types to arguments in
> methods that are unused (they are present make them compatible with
> another class, which uses them;
which is where I think mutually recursive classes would be usefull, if 
their typing was easier.
>  or they are required by the GUI
> layer), and that OCaml has now way to seperate method/function
> declaration and type declaration (unless one uses a mli file, which is
> again inconvenient because it splits information that belongs together
> into two complete different places). I'd really appreciate it if one
> could just mix "val" type declcarations with "let" or "method" code
> declarations.
>   
I don't understand what you want here.
>   
>> Of course, the resulting classes should be reusable, that is, one
>> should be able to extend a collection of related classes
>> simultaneously, such that the fields now have the relevant subtype
>> instead of the type they had before.
>>     
>
> Not sure what exactly you mean here. Could you give an example?
>   
I will try to state at an abstract level what I would like to do :

- define parametric virtual classes A and B so that every A has a Bs 
(one or many...) and vice-versa.
- possibly extend A and B to A' and B' respectively, so that every A' 
has B's (and not just Bs), etc.
- subtyping: possibly extend B to B1 and B2 so that their objects have 
As, but those As still have Bs, so that I can associate the same A to 
objects of class B, B1 or B2.

- and the ultimate goal combining all that: define A and B as above, 
other virtual classes C and D in a similar way and E and F too, and 
define concrete classes X Y Z1 Z2 just by saying that X extends A, Y 
will play the role of B in the first two classes and the role of C in 
the last two ones, and Z1 Z2 extends D (both) and E and F 
(respectively). It happens that some of the types that were left 
parametric (respectively methods defined as virtual) in B are made 
concrete in C, (and so on), so I obtain my final concrete classes.

Finally, I want the typing to be done step by step, so that for example 
the incompatibilities that exist already between A and B are detected 
independently of the rest.

I understand that what I'm asking for is a lot. The subtyping is 
especially problematic, and I begin to wonder if it's not related to the 
famous cases of inheritance that do not imply subtyping...

For now, I found many introductions on ocaml objects using and how to 
actually use them (Ocaml manual, Didier Remy's course, the "Developing 
applications with Objective Caml" book, as suggested by Julien Moutinho, 
and also Philippe Narbel's book) but none of them went that far.

Tiphaine Turpin


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

* Re: [Caml-list] OO programming
  2008-02-25  9:23   ` Tiphaine.Turpin
@ 2008-02-25 15:48     ` Edgar Friendly
  2008-02-25 16:02       ` Berke Durak
  2008-02-25 20:10     ` Dirk Thierbach
  1 sibling, 1 reply; 32+ messages in thread
From: Edgar Friendly @ 2008-02-25 15:48 UTC (permalink / raw)
  To: Tiphaine.Turpin; +Cc: caml-list

Tiphaine.Turpin wrote:
>>  or they are required by the GUI
>> layer), and that OCaml has now way to seperate method/function
>> declaration and type declaration (unless one uses a mli file, which is
>> again inconvenient because it splits information that belongs together
>> into two complete different places). I'd really appreciate it if one
>> could just mix "val" type declcarations with "let" or "method" code
>> declarations.
>>   
> I don't understand what you want here.

I read him as asking for .ml files to contain what .mli files now
contain - i.e. method/function declarations

Example - string_annex.ml:

val init: int -> (int -> char) -> string

let init n f =
  let s = make n (f 0) in
  for i=1 to n-1 do
    set s i (f i);
  done;
  s


OCaml doesn't permit this at the moment because val statements go in
.mli files (or in sig blocks) only.

E.


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

* Re: [Caml-list] OO programming
  2008-02-25 15:48     ` Edgar Friendly
@ 2008-02-25 16:02       ` Berke Durak
  2008-02-25 20:12         ` Dirk Thierbach
  0 siblings, 1 reply; 32+ messages in thread
From: Berke Durak @ 2008-02-25 16:02 UTC (permalink / raw)
  To: Edgar Friendly; +Cc: Tiphaine.Turpin, caml-list

Edgar Friendly a écrit :
> Tiphaine.Turpin wrote:
>>>  or they are required by the GUI
>>> layer), and that OCaml has now way to seperate method/function
>>> declaration and type declaration (unless one uses a mli file, which is
>>> again inconvenient because it splits information that belongs together
>>> into two complete different places). I'd really appreciate it if one
>>> could just mix "val" type declcarations with "let" or "method" code
>>> declarations.
>>>   
>> I don't understand what you want here.
> 
> I read him as asking for .ml files to contain what .mli files now
> contain - i.e. method/function declarations
> 
> Example - string_annex.ml:
> 
> val init: int -> (int -> char) -> string
> 
> let init n f =
>   let s = make n (f 0) in
>   for i=1 to n-1 do
>     set s i (f i);
>   done;
>   s
> 
> 
> OCaml doesn't permit this at the moment because val statements go in
> .mli files (or in sig blocks) only.
> 

Yes that could be nice but it's no biggie IMHO.

  let init n f =
    let s = make n (f 0) in
    for i=1 to n-1 do
      set s i (f i);
    done;
    s

  let init : int -> (int -> char) -> string = init

-- 
Berke DURAK


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

* Re: [Caml-list] OO programming
  2008-02-25  9:23   ` Tiphaine.Turpin
  2008-02-25 15:48     ` Edgar Friendly
@ 2008-02-25 20:10     ` Dirk Thierbach
  2008-02-25 21:49       ` Tiphaine.Turpin
  2008-02-29 14:22       ` Tiphaine.Turpin
  1 sibling, 2 replies; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 20:10 UTC (permalink / raw)
  To: caml-list

On Mon, Feb 25, 2008 at 10:23:09AM +0100, Tiphaine.Turpin wrote:
> Dirk Thierbach a écrit :
> >On Thu, Feb 21, 2008 at 10:31:42AM +0100, Tiphaine Turpin wrote:

> No connecting of objects, therefore no mutually recursive methods, (and 
> many arguments added to every function to carry the missing context), 
> little mutable state, and no use of class names to specify types... I'm 
> sure that many problems go away in this setting, and there are still 
> advantages to using objects (inheritance, etc.) 

Let me repeat: The main advantage of OO is that you can group related
state together, and that you can "push" changes (events, secondary
changes caused by events, whatever) onto this network of state.

While the functional way (especially with lazy computations) is to
"pull" a result from a network of computations. 

> but isn't this a somewhat degraded version of the object paradigm ?

Well, people can't seem to agree what exactly the "object paradigm"
actually is in the first place :-). Personally, I don't care. I want
clean, flexible, decoupled code. I am not interested in doing something
just because eomeone tells me, possible mistakenly, that I have to
follow "the" paradigm.

>> Much more annoying is that one also has to give types to arguments in
>> methods that are unused (they are present make them compatible with
>> another class, which uses them;

> which is where I think mutually recursive classes would be usefull, if 
> their typing was easier.

Mutual recursive classes won't solve this problem at all. Maybe an 
example helps: In a GUI, one can get various sorts of events, say,
a "resize" event. If the method connected to that event is only
interested in the new width and not in the height, so it doesn't
use this value at all, you have to declare the type. No other class
involved at all.

>> I'd really appreciate it if one could just mix "val" type
>> declcarations with "let" or "method" code declarations.

> I don't understand what you want here.

See Edgar's response. But that was only just a side-remark.

>>> Of course, the resulting classes should be reusable, that is, one
>>> should be able to extend a collection of related classes
>>> simultaneously, such that the fields now have the relevant subtype
>>> instead of the type they had before.

>> Not sure what exactly you mean here. Could you give an example?

> I will try to state at an abstract level what I would like to do :
> 
> - define parametric virtual classes A and B so that every A has a Bs 
> (one or many...) and vice-versa.
> - possibly extend A and B to A' and B' respectively, so that every A' 
> has B's (and not just Bs), etc.
> - subtyping: possibly extend B to B1 and B2 so that their objects have 
> As, but those As still have Bs, so that I can associate the same A to 
> objects of class B, B1 or B2.
> 
> - and the ultimate goal combining all that: define A and B as above, 
> other virtual classes C and D in a similar way and E and F too, and 
> define concrete classes X Y Z1 Z2 just by saying that X extends A, Y 
> will play the role of B in the first two classes and the role of C in 
> the last two ones, and Z1 Z2 extends D (both) and E and F 
> (respectively). It happens that some of the types that were left 
> parametric (respectively methods defined as virtual) in B are made 
> concrete in C, (and so on), so I obtain my final concrete classes.

If you manage to find a simple type system for that, preferably with
type inference and principal typings, I'd like to see it :-)

> For now, I found many introductions on ocaml objects using and how to 
> actually use them (Ocaml manual, Didier Remy's course, the "Developing 
> applications with Objective Caml" book, as suggested by Julien Moutinho, 
> and also Philippe Narbel's book) but none of them went that far.

So maybe there's a reason they are doing it differently. :-) You
can fight the system, and try to make everything difficult because you
think "it ought to be this way", or you can go the easy route. Your
choice. I can only tell you what I found that works for me.

- Dirk


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

* Re: [Caml-list] OO programming
  2008-02-25 16:02       ` Berke Durak
@ 2008-02-25 20:12         ` Dirk Thierbach
  2008-02-25 20:51           ` Tiphaine.Turpin
  0 siblings, 1 reply; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 20:12 UTC (permalink / raw)
  To: caml-list

On Mon, Feb 25, 2008 at 05:02:22PM +0100, Berke Durak wrote:
> Yes that could be nice but it's no biggie IMHO.
> 
>  let init n f =
>    let s = make n (f 0) in
>    for i=1 to n-1 do
>      set s i (f i);
>    done;
>    s
> 
>  let init : int -> (int -> char) -> string = init

Nice. I hadn't seen this trick before. You don't happen to know a
similar trick that works with methods?

- Dirk


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

* Re: [Caml-list] OO programming
  2008-02-25 20:12         ` Dirk Thierbach
@ 2008-02-25 20:51           ` Tiphaine.Turpin
  2008-02-25 23:03             ` Dirk Thierbach
  0 siblings, 1 reply; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-25 20:51 UTC (permalink / raw)
  To: Dirk Thierbach; +Cc: caml-list

Dirk Thierbach a écrit :
> On Mon, Feb 25, 2008 at 05:02:22PM +0100, Berke Durak wrote:
>   
>> Yes that could be nice but it's no biggie IMHO.
>>
>>  let init n f =
>>    let s = make n (f 0) in
>>    for i=1 to n-1 do
>>      set s i (f i);
>>    done;
>>    s
>>
>>  let init : int -> (int -> char) -> string = init
>>     
>
> Nice. I hadn't seen this trick before. You don't happen to know a
> similar trick that works with methods?
>   
I get the idea. Would the following satisfy your needs (you may place 
the code and the type in any order) ?

let _ = object (self)

  method init n f =
    let s = make n (f 0) in
      for i=1 to n-1 do
        set s i (f i);
      done;
      s

  method virtual init : int -> (int -> char) -> string

end

Tiphaine Turpin

> - Dirk
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>   


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

* Re: [Caml-list] OO programming
  2008-02-25 20:10     ` Dirk Thierbach
@ 2008-02-25 21:49       ` Tiphaine.Turpin
  2008-02-25 23:07         ` Dirk Thierbach
  2008-02-29 14:22       ` Tiphaine.Turpin
  1 sibling, 1 reply; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-25 21:49 UTC (permalink / raw)
  To: Dirk Thierbach; +Cc: caml-list

Dirk Thierbach a écrit :
> I am not interested in doing something
> just because eomeone tells me, possible mistakenly, that I have to
> follow "the" paradigm.
>   
I won't blame you for that. I'm not either :-). The few (2-3) programs 
of "not-toy" size that I did using objects (either in Java or in Ocaml) 
both extensively used many linked objects implementing lots of methods 
in a mutually recursive way, so I consider this an crucial features of 
objects. And really, I code things the way I think I'm able to do them 
the best (I have the chance that my programs only need to look nice to 
me): I do what I can. I choose functional or imperative style according 
to my best understanding of the domain, or objects (in Ocaml, I consider 
objects only if I think that I cannot achieve my goals in a satisfactory 
way with functions).

>   
>>> Much more annoying is that one also has to give types to arguments in
>>> methods that are unused (they are present make them compatible with
>>> another class, which uses them;
>>>       
>
>   
>> which is where I think mutually recursive classes would be usefull, if 
>> their typing was easier.
>>     
>
> Mutual recursive classes won't solve this problem at all.
Yes it does, I think (wether it's a good way of solving it is 
debatable). If you declare the classes generating events together with 
classes that handle them, and if it appears (in the declaration, which 
is where fields are usefull) that the method used in the generating 
objects will be invoked on the handler objects, then the types of the 
two methods will need to be compatible. Of course, in your example, you 
would probably want to use either a class type for handler objects, or 
have them inherit from a virtual handler class that declare the type of 
the method once and for all. My point is that using in one of the 
classes the type associated with the other class for one of its fields 
(and invoking methods on that fields) puts the necessary constraints to 
free you from many declarations (mutual recursive classes are actually 
only needed if you want links in both directions, which is not 
necessarily the case of your example, I agree).


>  Maybe an 
> example helps: In a GUI, one can get various sorts of events, say,
> a "resize" event. If the method connected to that event is only
> interested in the new width and not in the height, so it doesn't
> use this value at all, you have to declare the type. No other class
> involved at all.
>   
>> I will try to state at an abstract level what I would like to do :
>>
>> - define parametric virtual classes A and B so that every A has a Bs 
>> (one or many...) and vice-versa.
>> - possibly extend A and B to A' and B' respectively, so that every A' 
>> has B's (and not just Bs), etc.
>> - subtyping: possibly extend B to B1 and B2 so that their objects have 
>> As, but those As still have Bs, so that I can associate the same A to 
>> objects of class B, B1 or B2.
>>
>> - and the ultimate goal combining all that: define A and B as above, 
>> other virtual classes C and D in a similar way and E and F too, and 
>> define concrete classes X Y Z1 Z2 just by saying that X extends A, Y 
>> will play the role of B in the first two classes and the role of C in 
>> the last two ones, and Z1 Z2 extends D (both) and E and F 
>> (respectively). It happens that some of the types that were left 
>> parametric (respectively methods defined as virtual) in B are made 
>> concrete in C, (and so on), so I obtain my final concrete classes.
>>     
>
> If you manage to find a simple type system for that, preferably with
> type inference and principal typings, I'd like to see it :-)
>   
I wish I could do that in Ocaml...

>> For now, I found many introductions on ocaml objects using and how to 
>> actually use them (Ocaml manual, Didier Remy's course, the "Developing 
>> applications with Objective Caml" book, as suggested by Julien Moutinho, 
>> and also Philippe Narbel's book) but none of them went that far.
>>     
>
> So maybe there's a reason they are doing it differently. :-)
They aren't doing differently. They're just not treating an exhaustive 
list of the software architecture problems that people may encouter (or, 
pose to themselve). Which does not scandalize me in itself ;-).


Tiphaine Turpin


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

* Re: [Caml-list] OO programming
  2008-02-25 20:51           ` Tiphaine.Turpin
@ 2008-02-25 23:03             ` Dirk Thierbach
  0 siblings, 0 replies; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 23:03 UTC (permalink / raw)
  To: caml-list

On Mon, Feb 25, 2008 at 09:51:14PM +0100, Tiphaine.Turpin wrote:
> I get the idea. Would the following satisfy your needs (you may place 
> the code and the type in any order) ?
> 
> let _ = object (self)
> 
>  method init n f =
>    let s = make n (f 0) in
>      for i=1 to n-1 do
>        set s i (f i);
>      done;
>      s
> 
>  method virtual init : int -> (int -> char) -> string
> 
> end

Yes, cool. Didn't think of virtual methods. Thanks.

- Dirk


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

* Re: [Caml-list] OO programming
  2008-02-25 21:49       ` Tiphaine.Turpin
@ 2008-02-25 23:07         ` Dirk Thierbach
  0 siblings, 0 replies; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 23:07 UTC (permalink / raw)
  To: caml-list

On Mon, Feb 25, 2008 at 10:49:49PM +0100, Tiphaine.Turpin wrote:
> The few (2-3) programs of "not-toy" size that I did using objects
> (either in Java or in Ocaml) both extensively used many linked
> objects implementing lots of methods in a mutually recursive way,

Then maybe one should look at one of those as a concrete example, and
it might be possible to decouple those interdependencies.

>>>> Much more annoying is that one also has to give types to arguments in
>>>> methods that are unused (they are present make them compatible with
>>>> another class, which uses them;

>> Mutual recursive classes won't solve this problem at all.

> Yes it does, I think (wether it's a good way of solving it is 
> debatable). If you declare the classes generating events together with 
> classes that handle them, 

I can't. First, they are not necessarily classes. Second, all this
stuff is provided by the GUI framework, which I cannot change.

- Dirk


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

* Re: [Caml-list] OO programming
  2008-02-21  9:31 OO programming Tiphaine Turpin
                   ` (2 preceding siblings ...)
  2008-02-24 16:33 ` [Caml-list] " Dirk Thierbach
@ 2008-02-26  6:17 ` Jacques Garrigue
  2008-02-26  9:36   ` Julien Signoles
  2008-02-27  0:25   ` Tiphaine.Turpin
  3 siblings, 2 replies; 32+ messages in thread
From: Jacques Garrigue @ 2008-02-26  6:17 UTC (permalink / raw)
  To: Tiphaine.Turpin; +Cc: caml-list

From: Tiphaine Turpin <Tiphaine.Turpin@irisa.fr>

> After a few unsuccessfull tries with using the object oriented features 
> of ocaml, I have been looking for ways to write classes that have a 
> chance to typecheck. The usual problem is that objects refer to other 
> objects, those objects may refer back to objects referring to them, then 
> objects refer to different objects, some of them having more methods 
> than others (subtyping), etc. and finally the programmer has to give a 
> type to all those beautifull mutable fields that are not fully 
> specified, or make them parametric. Of course, the resulting classes 
> should be reusable, that is, one should be able to extend a collection 
> of related classes simultaneously, such that the fields now have the 
> relevant subtype instead of the type they had before.

I must say first that I completely agree with Dirk Thierbach,
the easy way to do that is to connect actions rather than objects.
This is the signal/slot approach to GUI programming, and in my opinion
this works much better because you don't have to bother about
interface incompatibilities: you just do the plumbing by hand, without
needing strange wrapper classes.

> The best guidelines that I found are in the following course from Didier 
> Remy :
> 
> http://caml.inria.fr/pub/docs/u3-ocaml/ocaml-objects.html#toc13
> 
> He uses parametric classes  (parametric in the type of the related 
> objects) so that they can be extended. However I'm still unsatisfied 
> with this solution, because the related classes are written 
> independently, or, more precisely, their dependencies remain in the head 
> of the programmer and have no concretization in the language. For 
> example if a class refer to a method provided by a related class, this 
> way of writing them does not guarantee that the method is actually 
> defined. Only when creating and linking the objects together will the 
> type-checker reject the program, if for example, the method has been 
> misspelled in one class. So for me this coding scheme has the drawback 
> that it unplugs the type-checker and just call it at the end. For me the 
> "ideal" use of objects would use mutually recursive classes with fields 
> defined with a type referring to the name of the other class. The 
> problem is that simply using the closed type of the other classs often 
> prevent any further refinement.

No, the type checker is not unplugged. Internal coherence is still
verified for each class, this just the coherence of their combination
that has to wait until you put them together. I don't see why it's
wrong: if you want to check the compatibilty, just write a few lines
combining the objects, and you will catch all the errors you want, at
compile time.
But I think your disagreement lies at a  different level: OO
programmers often want to express their own view of things directly
into the language. So the problem is not about safety, or even
efficiency of debugging, but about writing down everything you want to
say where you want to write it. Reading the following paper made
clearer a feeling I already had before:

  Klaus Ostermann. Nominal and structural subtyping in component-based
  programming. Journal of Object Technology, 7(1):121 - 145, 2008.

There, he explains that he wants the ability both to declare that a
class implements an interface (when the class is defined), or that an
interface is implemented by a class (when the interface is
defined). He then goes on saying that structural subtyping in ocaml
provides neither. But this is false. The point in ocaml is that
you don't need to declare anything. But if you want to be sure, you
can check the subtyping relation:

  let _check x = (x : subtype :> supertype)

You can write this anywhere. This is a check, not a declaration.
If there are type parameters, you need to write a bit more:

  module Check : sig val f : 'a subtype -> 'a supertype end =
    struct let f x = (x : _ subtype :> _ supertype) end

This is because type annotations in ocaml do not enforce that variables
are kept polymorphic. I you want to check it, you have to repeat
yourself at the module level.

> Hence my question: does anyone knows a way of combining the reusability 
> of sets of related classes with a more modular (type/consistency)-checking ?

I'm not sure whether it helps, but I attach here the same example of
observer pattern as in the tutorial, but without using type
parameters. They are replaced by private row types.

Ideally, one would like to check coherence too by writing
  module rec Check : S' = Window(Check)
Unfortunately, this doesn't seem to work currently. I'm not yet sure
whether this is a bug or a limitation of recursive modules.

---------------------------------------------------------------------------
Jacques Garrigue      Nagoya University     garrigue at math.nagoya-u.ac.jp
		   <A HREF=http://www.math.nagoya-u.ac.jp/~garrigue/>JG</A>

module type S = sig
  type event
  type subject
  type observer = private <notify: subject -> event -> unit; ..>
end

module Any(X:S) = struct
  class virtual observer =
    object
      method virtual notify : X.subject ->  X.event -> unit
    end
  class virtual subject =
    object (self)
      val mutable observers : X.observer list = []
      method add_observer obs = observers <- (obs :: observers)
      method notify_observers (e : X.event) = 
        List.iter (fun x -> x#notify self#self e) observers
    end
end

module type S' = sig
  type event = private [> `Move]
  type subject = private <draw: unit; ..>
  type observer = private <notify: subject -> event -> unit; ..>
end

module Window(X:S') = struct
  module AX = Any(X)
  class observer =
    object
      inherit AX.observer
      method notify s e = s#draw
    end
  let count = ref 0
  class virtual subject =
    let id = count := succ !count; !count in
    object (self)
      inherit AX.subject
      val mutable position = 0
      method identity = id
      method move x = position <- position + x; self#notify_observers `Move
      method draw = Printf.printf "{Position = %d}\n"  position;
    end
end

module WindowA = struct
  type event = [`Move]
  class type observer =
    object method notify : subject -> event -> unit end
  and subject =
    object
      method add_observer : observer -> unit
      method draw : unit
      method identity : int
      method move : int -> unit
      method notify_observers : event -> unit
    end
end

module WindowF : sig
  class observer : WindowA.observer
  class subject : WindowA.subject
end = struct
  module WF = Window(WindowA)
  class observer = WF.observer
  class subject = object (self)
    inherit WF.subject
    method private self = (self :> WindowA.subject)
  end
end

let window = new WindowF.subject;;
let window_observer = new WindowF.observer;;
window#add_observer window_observer;;
window#move 1;;

module WRichT = struct
  type event = [`Raise | `Resize | `Move]
  class type ['subject, 'event] observer =
    object method notify : 'subject -> 'event -> unit end
  and ['observer,'event] subject =
    object
      method add_observer : 'observer -> unit
      method draw : unit
      method identity : int
      method move : int -> unit
      method notify_observers : 'event -> unit
      method raise : unit
      method resize : int -> unit
    end
end

module type S'' = sig
  type event = private [> WRichT.event]
  type observer = private (subject, event) #WRichT.observer
  and subject = private (observer, event) #WRichT.subject
end

module WRich (X : S'') = struct
  module WRF = Window(X)
  class observer =
    object 
      inherit WRF.observer as super
      method notify s e = if e <> `Raise then s#raise; super#notify s e
    end
  let string_of_event = function
      `Raise -> "Raise" | `Resize -> "Resize" | `Move -> "Move"
    | _ -> "Unknown"
  class trace = 
    object 
      inherit WRF.observer
      method notify s e =
        Printf.printf
          "<Window %d <== %s>\n" s#identity (string_of_event e)
    end
  class virtual subject =
    object (self)
      inherit WRF.subject
      val mutable size = 1
      method resize x = size <- size + x; self#notify_observers `Resize
      val mutable top = false
      method raise = top <- true; self#notify_observers `Raise
      method draw =
        Printf.printf "{Position = %d; Size = %d}\n"  position size;
    end
end

module WRichA = struct
  type event = [`Raise | `Resize | `Move]
  class type observer = [subject, event] WRichT.observer
  and subject = [observer, event] WRichT.subject
end
module WRichF : sig
  class observer : WRichA.observer
  class trace : WRichA.observer
  class subject : WRichA.subject
end = struct
  module WRF = WRich(WRichA)
  class observer = WRF.observer
  let string_of_event = WRF.string_of_event
  class trace = WRF.trace
  class subject = object (self)
    inherit WRF.subject
    method private self = (self :> WRichA.subject)
  end
end

let window = new WRichF.subject ;;
window#add_observer (new WRichF.observer);;
window#add_observer (new WRichF.trace);;
window#move 1; window#resize 2;;


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

* Re: [Caml-list] OO programming
  2008-02-26  6:17 ` Jacques Garrigue
@ 2008-02-26  9:36   ` Julien Signoles
  2008-02-27  0:25   ` Tiphaine.Turpin
  1 sibling, 0 replies; 32+ messages in thread
From: Julien Signoles @ 2008-02-26  9:36 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: caml-list


>> Hence my question: does anyone knows a way of combining the reusability
>> of sets of related classes with a more modular (type/consistency)-checking ?
>
> I'm not sure whether it helps, but I attach here the same example of
> observer pattern as in the tutorial, but without using type
> parameters. They are replaced by private row types.
>
> Ideally, one would like to check coherence too by writing
>  module rec Check : S' = Window(Check)
> Unfortunately, this doesn't seem to work currently. I'm not yet sure
> whether this is a bug or a limitation of recursive modules.

As far as I understand the Jacques' code, the following check seems to 
work

===
module rec Check : sig
   type event = private [> `Move]
   type subject = private <draw: unit; ..>
   type observer = private < notify : Check.subject -> Check.event -> unit; .. >
end = struct
   include Window(Check)
   type event = Check.event
end
===

I don't know exactly why one has to write "Check.subject" and 
"Check.event" in the signature. The consequence is that the following 
code just does not work.

===
module rec Check : S' = struct
   include Window(Check)
   type event = Check.event
end
===

As Jacques said, it is either a bug or a limitation of recursive modules.
I don't know.

--
Julien


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

* Re: [Caml-list] OO programming
  2008-02-26  6:17 ` Jacques Garrigue
  2008-02-26  9:36   ` Julien Signoles
@ 2008-02-27  0:25   ` Tiphaine.Turpin
  2008-02-27  1:37     ` Jacques Garrigue
  2008-02-27  7:40     ` Dirk Thierbach
  1 sibling, 2 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-27  0:25 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: Tiphaine.Turpin, caml-list

Jacques Garrigue a écrit :
> From: Tiphaine Turpin <Tiphaine.Turpin@irisa.fr>
>
>   
>> After a few unsuccessfull tries with using the object oriented features 
>> of ocaml, I have been looking for ways to write classes that have a 
>> chance to typecheck. The usual problem is that objects refer to other 
>> objects, those objects may refer back to objects referring to them, then 
>> objects refer to different objects, some of them having more methods 
>> than others (subtyping), etc. and finally the programmer has to give a 
>> type to all those beautifull mutable fields that are not fully 
>> specified, or make them parametric. Of course, the resulting classes 
>> should be reusable, that is, one should be able to extend a collection 
>> of related classes simultaneously, such that the fields now have the 
>> relevant subtype instead of the type they had before.
>>     
>
> I must say first that I completely agree with Dirk Thierbach,
> the easy way to do that is to connect actions rather than objects.
> This is the signal/slot approach to GUI programming, and in my opinion
> this works much better because you don't have to bother about
> interface incompatibilities: you just do the plumbing by hand, without
> needing strange wrapper classes.
>   
First, my desired use of objects is not (reduced to) programming a gui. 
I can imagine that, in guis, the actions that may be invoked on objects 
or that may originate from them are just signals with rather simple 
types, which form a simple case of object communication for which action 
connection is enough, but when it comes to using objects to model the 
objects of the application domain, stronger links (i.e., fields) are 
sometimes natural. Note that, in class diagrams, links between classes 
are one of the most basic concepts.

>   
>> The best guidelines that I found are in the following course from Didier 
>> Remy :
>>
>> http://caml.inria.fr/pub/docs/u3-ocaml/ocaml-objects.html#toc13
>>
>> He uses parametric classes  (parametric in the type of the related 
>> objects) so that they can be extended. However I'm still unsatisfied 
>> with this solution, because the related classes are written 
>> independently, or, more precisely, their dependencies remain in the head 
>> of the programmer and have no concretization in the language. For 
>> example if a class refer to a method provided by a related class, this 
>> way of writing them does not guarantee that the method is actually 
>> defined. Only when creating and linking the objects together will the 
>> type-checker reject the program, if for example, the method has been 
>> misspelled in one class. So for me this coding scheme has the drawback 
>> that it unplugs the type-checker and just call it at the end. For me the 
>> "ideal" use of objects would use mutually recursive classes with fields 
>> defined with a type referring to the name of the other class. The 
>> problem is that simply using the closed type of the other classs often 
>> prevent any further refinement.
>>     
>
> No, the type checker is not unplugged. Internal coherence is still
> verified for each class, this just the coherence of their combination
> that has to wait until you put them together.
Yes, of course. The word (unplugged was a provocation ;-)).

>  I don't see why it's
> wrong: if you want to check the compatibilty, just write a few lines
> combining the objects, and you will catch all the errors you want, at
> compile time.
>   
Yes, writing such a "challenge" just after the definition (and before 
continuing to extend things) looks less beautifull than we would like, 
but it should work (I didn't try it).

> But I think your disagreement lies at a  different level: OO
> programmers often want to express their own view of things directly
> into the language.
Yes. Clearly, I want the abstractions, patterns, etc. I have in mind to 
be expressed themselves as language objects, and not just their 
"instanciation". Isn't this the very goal of high-level languages after 
all ?
>  So the problem is not about safety, or even
> efficiency of debugging, but about writing down everything you want to
> say where you want to write it. Reading the following paper made
> clearer a feeling I already had before:
>
>   Klaus Ostermann. Nominal and structural subtyping in component-based
>   programming. Journal of Object Technology, 7(1):121 - 145, 2008.
>
>   
An interesting reading. The expressive power that he adds to nominal 
subtyping gives me more arguments to consider structural subtyping not 
very usefull (but I understand that structural is much more consistent 
with everything else in ocaml).
> here, he explains that he wants the ability both to declare that a
> class implements an interface (when the class is defined), or that an
> interface is implemented by a class (when the interface is
> defined). He then goes on saying that structural subtyping in ocaml
> provides neither. But this is false. The point in ocaml is that
> you don't need to declare anything. But if you want to be sure, you
> can check the subtyping relation:
>
>   let _check x = (x : subtype :> supertype)
>
> You can write this anywhere. This is a check, not a declaration.
> If there are type parameters, you need to write a bit more:
>
>   module Check : sig val f : 'a subtype -> 'a supertype end =
>     struct let f x = (x : _ subtype :> _ supertype) end
>
> This is because type annotations in ocaml do not enforce that variables
> are kept polymorphic. I you want to check it, you have to repeat
> yourself at the module level.
>
>   
>> Hence my question: does anyone knows a way of combining the reusability 
>> of sets of related classes with a more modular (type/consistency)-checking ?
>>     
>
> I'm not sure whether it helps, but I attach here the same example of
> observer pattern as in the tutorial,
which tutorial ?

>  but without using type
> parameters. They are replaced by private row types.
>   
which is very verbose (having to explicitely write class types), and I'm 
not sure I understand the benefits of doing this.
> Ideally, one would like to check coherence too by writing
>   module rec Check : S' = Window(Check)
> Unfortunately, this doesn't seem to work currently. I'm not yet sure
> whether this is a bug or a limitation of recursive modules.
>   
If the typing of recursive modules just types the body of each module in 
the environment given by the declared module types of all modules 
involved in the recursive definition, then this cannot work, since 
Window(Check) would have to be of type S' for any Check : S', which is 
clearly false (e.g. Check = struct ... type  observer = <foo: unit; 
notify : ...).

I have a hard time understanding your code, and'm still unsure of the 
rationale. So, instead of recursive classes, you write a functor of 
which the classes are the fix point ? And the private row types are here 
just to allow you to add structural contraints on types in a module type 
? The functors have the advantage that they group the classes together 
in "something", and forcing the relation between their parameters in S'' 
is interesting. Too bad that your module rec doesn't work...

As for extension, I'm fully satisfied. But the verbosity level is 
annoying for scalability...

Tiphaine Turpin
> ---------------------------------------------------------------------------
> Jacques Garrigue      Nagoya University     garrigue at math.nagoya-u.ac.jp
> 		   <A HREF=http://www.math.nagoya-u.ac.jp/~garrigue/>JG</A>
>
> module type S = sig
>   type event
>   type subject
>   type observer = private <notify: subject -> event -> unit; ..>
> end
>
> module Any(X:S) = struct
>   class virtual observer =
>     object
>       method virtual notify : X.subject ->  X.event -> unit
>     end
>   class virtual subject =
>     object (self)
>       val mutable observers : X.observer list = []
>       method add_observer obs = observers <- (obs :: observers)
>       method notify_observers (e : X.event) = 
>         List.iter (fun x -> x#notify self#self e) observers
>     end
> end
>
> module type S' = sig
>   type event = private [> `Move]
>   type subject = private <draw: unit; ..>
>   type observer = private <notify: subject -> event -> unit; ..>
> end
>
> module Window(X:S') = struct
>   module AX = Any(X)
>   class observer =
>     object
>       inherit AX.observer
>       method notify s e = s#draw
>     end
>   let count = ref 0
>   class virtual subject =
>     let id = count := succ !count; !count in
>     object (self)
>       inherit AX.subject
>       val mutable position = 0
>       method identity = id
>       method move x = position <- position + x; self#notify_observers `Move
>       method draw = Printf.printf "{Position = %d}\n"  position;
>     end
> end
>
> module WindowA = struct
>   type event = [`Move]
>   class type observer =
>     object method notify : subject -> event -> unit end
>   and subject =
>     object
>       method add_observer : observer -> unit
>       method draw : unit
>       method identity : int
>       method move : int -> unit
>       method notify_observers : event -> unit
>     end
> end
>
> module WindowF : sig
>   class observer : WindowA.observer
>   class subject : WindowA.subject
> end = struct
>   module WF = Window(WindowA)
>   class observer = WF.observer
>   class subject = object (self)
>     inherit WF.subject
>     method private self = (self :> WindowA.subject)
>   end
> end
>
> let window = new WindowF.subject;;
> let window_observer = new WindowF.observer;;
> window#add_observer window_observer;;
> window#move 1;;
>
> module WRichT = struct
>   type event = [`Raise | `Resize | `Move]
>   class type ['subject, 'event] observer =
>     object method notify : 'subject -> 'event -> unit end
>   and ['observer,'event] subject =
>     object
>       method add_observer : 'observer -> unit
>       method draw : unit
>       method identity : int
>       method move : int -> unit
>       method notify_observers : 'event -> unit
>       method raise : unit
>       method resize : int -> unit
>     end
> end
>
> module type S'' = sig
>   type event = private [> WRichT.event]
>   type observer = private (subject, event) #WRichT.observer
>   and subject = private (observer, event) #WRichT.subject
> end
>
> module WRich (X : S'') = struct
>   module WRF = Window(X)
>   class observer =
>     object 
>       inherit WRF.observer as super
>       method notify s e = if e <> `Raise then s#raise; super#notify s e
>     end
>   let string_of_event = function
>       `Raise -> "Raise" | `Resize -> "Resize" | `Move -> "Move"
>     | _ -> "Unknown"
>   class trace = 
>     object 
>       inherit WRF.observer
>       method notify s e =
>         Printf.printf
>           "<Window %d <== %s>\n" s#identity (string_of_event e)
>     end
>   class virtual subject =
>     object (self)
>       inherit WRF.subject
>       val mutable size = 1
>       method resize x = size <- size + x; self#notify_observers `Resize
>       val mutable top = false
>       method raise = top <- true; self#notify_observers `Raise
>       method draw =
>         Printf.printf "{Position = %d; Size = %d}\n"  position size;
>     end
> end
>
> module WRichA = struct
>   type event = [`Raise | `Resize | `Move]
>   class type observer = [subject, event] WRichT.observer
>   and subject = [observer, event] WRichT.subject
> end
> module WRichF : sig
>   class observer : WRichA.observer
>   class trace : WRichA.observer
>   class subject : WRichA.subject
> end = struct
>   module WRF = WRich(WRichA)
>   class observer = WRF.observer
>   let string_of_event = WRF.string_of_event
>   class trace = WRF.trace
>   class subject = object (self)
>     inherit WRF.subject
>     method private self = (self :> WRichA.subject)
>   end
> end
>
> let window = new WRichF.subject ;;
> window#add_observer (new WRichF.observer);;
> window#add_observer (new WRichF.trace);;
> window#move 1; window#resize 2;;
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>   


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

* Re: [Caml-list] OO programming
  2008-02-27  0:25   ` Tiphaine.Turpin
@ 2008-02-27  1:37     ` Jacques Garrigue
  2008-02-28  8:34       ` Keiko Nakata
  2008-02-27  7:40     ` Dirk Thierbach
  1 sibling, 1 reply; 32+ messages in thread
From: Jacques Garrigue @ 2008-02-27  1:37 UTC (permalink / raw)
  To: Tiphaine.Turpin, caml-list

From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>

> First, my desired use of objects is not (reduced to) programming a gui. 
> I can imagine that, in guis, the actions that may be invoked on objects 
> or that may originate from them are just signals with rather simple 
> types, which form a simple case of object communication for which action 
> connection is enough, but when it comes to using objects to model the 
> objects of the application domain, stronger links (i.e., fields) are 
> sometimes natural. Note that, in class diagrams, links between classes 
> are one of the most basic concepts.

My point was that it's not because it is a good concept that it is
necessarily a good programming technique. That is, you might want to
right your class diagram with links between classes, but use an
indirection at the connection level in your implementation, if it
makes things simpler. I see no contradiction there. But as I wrote in
my previous mail, OO people often prefer to have their concepts
directly apparent in their programs.

> > But I think your disagreement lies at a  different level: OO
> > programmers often want to express their own view of things directly
> > into the language.
> Yes. Clearly, I want the abstractions, patterns, etc. I have in mind to 
> be expressed themselves as language objects, and not just their 
> "instanciation". Isn't this the very goal of high-level languages after 
> all ?

Yes, but the ocaml approach is to define entities independently, and
let types handle the interconnections. This does not fit well with
class diagrams. So if you want to keep your class diagram apparent,
you need painful contorsions.

Note also that your original goal, being able to extend families of
classes at the type level, is an active research area. When ocaml was
created, there was no OO language able to do it explicitly, or even
implicitly (as ocaml does). Now you might have your chance with Scala
or gBeta.

> >   Klaus Ostermann. Nominal and structural subtyping in component-based
> >   programming. Journal of Object Technology, 7(1):121 - 145, 2008.
> >   
> An interesting reading. The expressive power that he adds to nominal 
> subtyping gives me more arguments to consider structural subtyping not 
> very usefull (but I understand that structural is much more consistent 
> with everything else in ocaml).

Not really. He build his nominal subtyping as an extension of
structural subtyping. And I'm personally not sure that the nominal
part gets him much (out of better error messages, which do matter)

> >> Hence my question: does anyone knows a way of combining the reusability 
> >> of sets of related classes with a more modular (type/consistency)-checking ?
> >>     
> >
> > I'm not sure whether it helps, but I attach here the same example of
> > observer pattern as in the tutorial,
> which tutorial ?

Section 5.3 of the reference manual, which is a variation on the one
you were mentionning.

> >  but without using type parameters. They are replaced by private row types.
> >   
> which is very verbose (having to explicitely write class types), and I'm 
> not sure I understand the benefits of doing this.

Verbose indeed. I didn't say this was the right way to do it, just
that it lets you express more relations in the interfaces.

> > Ideally, one would like to check coherence too by writing
> >   module rec Check : S' = Window(Check)
> > Unfortunately, this doesn't seem to work currently. I'm not yet sure
> > whether this is a bug or a limitation of recursive modules.
> >   
> If the typing of recursive modules just types the body of each module in 
> the environment given by the declared module types of all modules 
> involved in the recursive definition, then this cannot work, since 
> Window(Check) would have to be of type S' for any Check : S', which is 
> clearly false (e.g. Check = struct ... type  observer = <foo: unit; 
> notify : ...).

No, the typing of recursive modules is much more clever, otherwise it
wouldn't be useful. So, conceptually, this could work.
For instance, if you drop the ability to add methods to the observer,
the check goes through:

module type S' = sig
  type event = private [> `Move]
  type subject = private <draw: unit; ..>
  type observer = <notify: subject -> event -> unit>
end

module Window(X:S') = struct
  module AX = Any(X)
  type event = X.event
  class observer =
    object
      inherit AX.observer
      method notify s e = s#draw
    end
  let count = ref 0
  class virtual subject =
    let id = count := succ !count; !count in
    object (self)
      inherit AX.subject
      val mutable position = 0
      method identity = id
      method move x = position <- position + x; self#notify_observers `Move
      method draw = Printf.printf "{Position = %d}\n"  position;
    end
end

module rec Check : S' = Window(Check)

This way you can see that subject and observer are compatible without
providing concrete instances.

However, when the observer and subject have both extensible types,
there seems to be an interaction between nominal and structural types,
and the knot cannot be tied.

> As for extension, I'm fully satisfied. But the verbosity level is 
> annoying for scalability...

Well, yes, that's always the problem with functors...

Jacques Garrigue


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

* Re: [Caml-list] OO programming
  2008-02-27  0:25   ` Tiphaine.Turpin
  2008-02-27  1:37     ` Jacques Garrigue
@ 2008-02-27  7:40     ` Dirk Thierbach
  2008-02-27 14:04       ` Tiphaine.Turpin
  1 sibling, 1 reply; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-27  7:40 UTC (permalink / raw)
  To: caml-list

On Wed, Feb 27, 2008 at 01:25:44AM +0100, Tiphaine.Turpin wrote:
> First, my desired use of objects is not (reduced to) programming a gui. 

When I said "let's look at a concrete example", I meant it. It might
be very instructive. If your program is too long, just give an high-level
overview of the situation, this should be enough for a start.

- Dirk


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

* Re: [Caml-list] OO programming
  2008-02-27  7:40     ` Dirk Thierbach
@ 2008-02-27 14:04       ` Tiphaine.Turpin
  0 siblings, 0 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-27 14:04 UTC (permalink / raw)
  To: caml-list

Dirk Thierbach a écrit :
> On Wed, Feb 27, 2008 at 01:25:44AM +0100, Tiphaine.Turpin wrote:
>   
>> First, my desired use of objects is not (reduced to) programming a gui. 
>>     
>
> When I said "let's look at a concrete example", I meant it. It might
> be very instructive. If your program is too long, just give an high-level
> overview of the situation, this should be enough for a start.
>   
I'm affraid my existing "real" code wouldn't be any usefull: it is very 
long indeed (>1000loc), fits mainly in one recursive definition of 
classes (with heavy and unstructured use of multiple inheritance and 
virtual classes), and is unreadable (application domain already complex, 
+ all features (gui, etc.) mixed in the same objects...), which led me 
to drop the project until I find new ideas for structuring it (which I 
am seeking here). The very abstract extension cases that stated earlier 
are the best solution that I can think of to put some structure in all 
that, and that's why I'm trying to get the possible best way of 
implementing this "pattern".

Tiphaine Turpin

> - Dirk
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>   


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

* Re: [Caml-list] OO programming
  2008-02-27  1:37     ` Jacques Garrigue
@ 2008-02-28  8:34       ` Keiko Nakata
  2008-02-28 13:30         ` Andrej Bauer
  2008-02-29 14:35         ` Tiphaine.Turpin
  0 siblings, 2 replies; 32+ messages in thread
From: Keiko Nakata @ 2008-02-28  8:34 UTC (permalink / raw)
  To: garrigue; +Cc: Tiphaine.Turpin, caml-list

Hello,

> > As for extension, I'm fully satisfied. But the verbosity level is 
> > annoying for scalability...
> 
> Well, yes, that's always the problem with functors...

Since there are some people (including me) 
who are interested in using functors and recursive modules 
in the style of object-oriented context, 
I thought that it could be useful to devise 
a (camlp4) syntax extension which mitigates this a bit painful verbosity. 

At the moment, I have no idea which syntax is general enough and intuitive for us,
but as far as I understand we always follow similar encodings. 

With best regards,
Keiko


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

* Re: [Caml-list] OO programming
  2008-02-28  8:34       ` Keiko Nakata
@ 2008-02-28 13:30         ` Andrej Bauer
  2008-02-28 15:18           ` Keiko Nakata
  2008-02-28 16:02           ` Edgar Friendly
  2008-02-29 14:35         ` Tiphaine.Turpin
  1 sibling, 2 replies; 32+ messages in thread
From: Andrej Bauer @ 2008-02-28 13:30 UTC (permalink / raw)
  To: Caml

Keiko Nakata wrote:
>>> As for extension, I'm fully satisfied. But the verbosity level is 
>>> annoying for scalability...
>> Well, yes, that's always the problem with functors...
> 
> Since there are some people (including me) 
> who are interested in using functors and recursive modules 
> in the style of object-oriented context, 
> I thought that it could be useful to devise 
> a (camlp4) syntax extension which mitigates this a bit painful verbosity. 
> 
> At the moment, I have no idea which syntax is general enough and intuitive for us,
> but as far as I understand we always follow similar encodings. 

I have three wishes related to the case when a functor accepts a 
structure that contains a single type or a single value:

1) To be able to write

   module F(type t) = struct ...t... end

instead of

   module F(T : sig type t end) = struct ... T.t  ... end

and to write

   F(s)

instead of

   F(struct type t = s end)

2) Similarly for values, to be able to write

   module F(val x : t) = struct ... x ... end

instead of

   module F(T : sig val x : t end) = struct ... T.x ... end

3) Similarly for signatures:

   module type F = functor type t -> sig ... end

   module type F = functor val x : t -> sig ... end

I believe these are campl4 trivialities. There may be some hoxus-pocus 
related to generating suitable names for T's above.

Andrej


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

* Re: [Caml-list] OO programming
  2008-02-28 13:30         ` Andrej Bauer
@ 2008-02-28 15:18           ` Keiko Nakata
  2008-02-28 16:02           ` Edgar Friendly
  1 sibling, 0 replies; 32+ messages in thread
From: Keiko Nakata @ 2008-02-28 15:18 UTC (permalink / raw)
  To: caml-list


> I have three wishes related to the case when a functor accepts a 
> structure that contains a single type or a single value:
> 
> 1) To be able to write
> 
>    module F(type t) = struct ...t... end
> 
> instead of
> 
>    module F(T : sig type t end) = struct ... T.t  ... end
> 
> and to write
> 
>    F(s)
> 
> instead of
> 
>    F(struct type t = s end)
> 
> 2) Similarly for values, to be able to write
> 
>    module F(val x : t) = struct ... x ... end
> 
> instead of
> 
>    module F(T : sig val x : t end) = struct ... T.x ... end
> 
> 3) Similarly for signatures:
> 
>    module type F = functor type t -> sig ... end
> 
>    module type F = functor val x : t -> sig ... end
> 
> I believe these are campl4 trivialities. There may be some hoxus-pocus 
> related to generating suitable names for T's above.

This is merely my personal preference, but I usually do not prefer 
implicit construction of names; for instance I may well get confused
by type error messages. 

Besides in this scenario:

>    module F(type t) = struct ...t... end

we need to decide which take precedence if the structure declares 
a type component named t. 

On the other hand, since the functor parameter 
has the role of the super class in the functor&fix-point approach 
to OO-style programming, it may well be useful 
to have an implicit way to refer to components of the parameter, 
as you proposed. Yet this may be confusing again if we consider 
multi-parameter functors a la multiple inheritance. 

What do you think?

With best regards,
Keiko


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

* Re: [Caml-list] OO programming
  2008-02-28 13:30         ` Andrej Bauer
  2008-02-28 15:18           ` Keiko Nakata
@ 2008-02-28 16:02           ` Edgar Friendly
  1 sibling, 0 replies; 32+ messages in thread
From: Edgar Friendly @ 2008-02-28 16:02 UTC (permalink / raw)
  To: Andrej.Bauer; +Cc: Caml

Andrej Bauer wrote:
> Keiko Nakata wrote:
>>>> As for extension, I'm fully satisfied. But the verbosity level is
>>>> annoying for scalability...
>>> Well, yes, that's always the problem with functors...
>>
>> Since there are some people (including me) who are interested in using
>> functors and recursive modules in the style of object-oriented
>> context, I thought that it could be useful to devise a (camlp4) syntax
>> extension which mitigates this a bit painful verbosity.
>> At the moment, I have no idea which syntax is general enough and
>> intuitive for us,
>> but as far as I understand we always follow similar encodings. 
> 
> I have three wishes related to the case when a functor accepts a
> structure that contains a single type or a single value:
> 
> 1) To be able to write
> 
>   module F(type t) = struct ...t... end
> 
> instead of
> 
>   module F(T : sig type t end) = struct ... T.t  ... end
> 
> and to write
> 
>   F(s)
> 
> instead of
> 
>   F(struct type t = s end)
> 
I wonder what use you would put this to where normal 'a polymorphism
wouldn't suffice.

> 2) Similarly for values, to be able to write
> 
>   module F(val x : t) = struct ... x ... end
> 
> instead of
> 
>   module F(T : sig val x : t end) = struct ... T.x ... end
> 
Similarly, an additional parameter on each of the contained functions
seems not too unreasonable.  I guess I can see more use for this than
#1, but a global ref inside F might also work, as long as you had a way
to set it and you had a good default.

E.


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

* Re: [Caml-list] OO programming
  2008-02-25 20:10     ` Dirk Thierbach
  2008-02-25 21:49       ` Tiphaine.Turpin
@ 2008-02-29 14:22       ` Tiphaine.Turpin
  1 sibling, 0 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-29 14:22 UTC (permalink / raw)
  To: caml-list

[-- Attachment #1: Type: text/plain, Size: 11367 bytes --]

Dirk Thierbach a écrit :
> On Mon, Feb 25, 2008 at 10:23:09AM +0100, Tiphaine.Turpin wrote:
>   
>> I will try to state at an abstract level what I would like to do :
>>
>> - define parametric virtual classes A and B so that every A has a Bs 
>> (one or many...) and vice-versa.
>> - possibly extend A and B to A' and B' respectively, so that every A' 
>> has B's (and not just Bs), etc.
>> - subtyping: possibly extend B to B1 and B2 so that their objects have 
>> As, but those As still have Bs, so that I can associate the same A to 
>> objects of class B, B1 or B2.
>>
>> - and the ultimate goal combining all that: define A and B as above, 
>> other virtual classes C and D in a similar way and E and F too, and 
>> define concrete classes X Y Z1 Z2 just by saying that X extends A, Y 
>> will play the role of B in the first two classes and the role of C in 
>> the last two ones, and Z1 Z2 extends D (both) and E and F 
>> (respectively). It happens that some of the types that were left 
>> parametric (respectively methods defined as virtual) in B are made 
>> concrete in C, (and so on), so I obtain my final concrete classes.
>>     
>
> If you manage to find a simple type system for that, preferably with
> type inference and principal typings, I'd like to see it :-)
>   

I made more extensive tests with my class extension problem, using only 
classes, and with a quite acceptable overhead for the programmer (no 
modules, no need to write row types by hand). So I'm giving the state of 
my understanding in this very long mail. I hope this will be of interest 
to the many of you who answered my questions and therefore contributed 
to it. The following is about achieving the programming schemes 
described above ; don't expect any concrete examples here, as I still 
have to try that and see wether it is any usefull for programming. 
Actually there are even almost no methods (especially virtual) in what 
follows, but I think this doesn't change the rules too much.

First, here is a way to define two classes a and b as above (the kind of 
link here is one to one association, but this is not important since the 
concern is about typing).

class ['b] a = object
  constraint 'b = _ #b

  val b : 'b option ref = ref None
  method set_b b' = b := Some b'
  method get_b = match ! b with
    | Some b -> b
    | None -> raise Not_found

end

and ['a] b = object
  constraint 'a = _ #a

  val a : 'a option ref = ref None
  method set_a a' = a := Some a'
  method get_a = match ! a with
    | Some a -> a
    | None -> raise Not_found

end

So the two classes are parameterized by the type of the objects they are 
associated with. Furthermore this type parameter is constrained to be an 
instance of the open type of the other class, as suggested by Remi 
Vanicat. This seemed very counter-intuitive to me to give an open type 
to the content of a reference (which I expect to be invariant). But it 
works with that, and not with the close type (the definition works, but 
extension doesn't).

More generally, when defining a set of related classes, each one should 
be parameterized by the classes it is direcly linked with.

A very strange thing happens when type-checking this: the constraint 'b 
= _ #b is replaced with the stronger constraint 'b = 'b #a #b (and 
symetrically in class b). I still don't understand why it happens, but 
it is close to what we would like to express. A simpler (stronger) 
constraint that one could think of is something like 'self = 'self b a 
(which is not allowed) or writing unparameterized recursive classes, but 
this is too strong for some usages, because then we cannot associate 
objects of type a with objects of a subtype of b. So I view the 
constraint 'a = 'a #a #b as meaning a similar thing except that "an 
object a may see itself with a stronger type than its associated object 
b sees a". So, this definition will allow subtyping of either of the two 
classes, or both.

Note that instead of the above choice of parameters, one can use the 
whole set of all related classes to parameterize each one. This works 
too. However, parameterizing each class just by itself (which should 
concentrate all parameters in a single one) leads to very different 
results: first, the above "magic" constraint does not appear, even if we 
declare (an adapted version of) it explicitely. Is it somewhat implied ? 
Anyway, this version produces a typing bug when using the resulting 
classes with coercions (bug #00004505). So, for me, the above version 
seems to be the best solution at this time.

The above classes work (i.e., type-checks) as expected:

let x = new a
and y = new b

let _ =
  x#set_b y;
  y#set_a x;
  x = x#get_b#get_a && y = y#get_a#get_b

The compatibility of the two classes is checked too at some level, but 
not completely. Precisely the methods used on one side and declared on 
the other side are checked to be compatible (so that we cannot forget 
argumets or things like that) but the actual existence of a used method 
is not checked. Conjecture: if any concrete classes declared as above 
type-check, then they may be extended in such a way that the connection 
of two new objects will type-check). However, there is a way to enforce 
the existence of used methods, for example with the following code:

let _check _ : 'b a * ('b a b as 'b) = new a, new b

This is similar to the ckecks suggested by Jacques Garrigue and Julien 
Signoles (except that no functors are involved). This works only for 
concrete classes, and this is not ideal since this only appears as a 
challenge and not in the types of the classes themselves, but this is a 
good start. Note that the onstraint 'a = 'self a would work if the two 
classes were not recursive. However, the obtained classes could not be 
extended. With mutually recursive classes, we get a "Self type cannot 
escape its class" error.

Things like the following may also work (but I'm not sure wether it is 
equivalent). Extension of a' and b' is not possible then.

class virtual ['b] a' = object (self : 'self)
  inherit ['b] a
  constraint 'b = 'self b
end

class virtual ['a] b' = object (self : 'self)
  inherit ['a] b
  constraint 'a = 'self a
end

The following code extends a and b "simultaneously", in the sense that 
a2 knows that the object it would get by calling self#get_b is of type 
b2, and thus has a compatible method bar.

class ['b] a2 = object
  constraint 'b = _ #b2

  inherit ['b] a
  method foo = ()

end

and ['a] b2 = object
  constraint 'a = _ #a2

  inherit ['a] b
  method bar = ()

end

The constraints are re-stated in terms of the new classes in order to 
check the compatibility of new methods. Here is an example of error that 
would have been detected:

    method bar = self#get_a#foo ()
                 ^^^^^^^^^^^^^^
This expression is not a function, it cannot be applied

We can also repeat the second check:

let _check _ : 'b a2 * ('b a2 b2 as 'b) = new a2, new b2


The two new classes may be used without trouble, as for the first two 
ones. Another extension we can do is to make several subclasses of one 
of our classes, say b:

class ['a] b' = object

  inherit ['a] b
  method foo = ()

end

and ['a] b'' = object

  inherit ['a] b
  method bar = ()

end

This time, we don't add any new constraint to class a, since it must 
still accept b objects of class b, as we want to be able to connect 
objects of classes both b' and b''. Here is an example:

let x = new a
and y1 = new b'
and y2 = new b''

let _ =
  x#set_b (y1 :> _ b);
  y1#set_a x;
  x#set_b (y2 :> _ b);
  y2#set_a x

let _ =
  x = x#get_b#get_a && (y2 :> _ b) = y2#get_a#get_b

Of course we don't even need to define classes for that. We could have 
used immediate objects. Back to extension, the following is an example 
where a and b are the same class:

class ['c] c = object
  inherit ['c] b
  inherit ['c] a
end

Here is an example of use with subtyping:

let x = object inherit [_] c method foo = () end
and y = object inherit [_] c method bar = () end

let _ =
  x#set_b (y :> _ c);
  x#set_a (y :> _ c);
  y#set_b (x :> _ c);
  y#set_a (x :> _ c)

let _ =
  (y :> _ c) = x#get_a
  && (y :> _ c) = x#get_b
  && (x :> _ c) = y#get_a
  && (x :> _ c) = y#get_b

let _ =
  (x :> _ c) <> (y :> _ c)

Rather that closing the association link  on itself we may also combine 
two instances of it (this would also work for two differents pairs of 
classes):

class ['j] i = object
  inherit ['j] a
  constraint 'j = (_, _) #j
end

and ['i, 'k] j = object
  inherit ['i] b
  inherit ['k] a
  constraint 'i = _ #i
  constraint 'k = _ #k
end

and ['j] k = object
  inherit ['j] b
  constraint 'j = (_, _) #j
end

let _check _ : 'j i * (('j i, 'j k) j as 'j) * 'j k = new i, new j, new k

This time, the check looks more complex. And finally, the complete 
example described at the top:

class ['b] a = object
  constraint 'b = 'b #a #b

  val b : 'b option ref = ref None
  method set_b b' = b := Some b'
  method get_b = match ! b with
    | Some b -> b
    | None -> raise Not_found

end

and virtual ['a] b = object
  constraint 'a = 'a #b #a

  val a : 'a option ref = ref None
  method set_a a' = a := Some a'
  method get_a = match ! a with
    | Some a -> a
    | None -> raise Not_found

  method virtual foo : _
end

(* The following cannot be checked, because b is virtual
let _check _ : 'b a * ('b a b as 'b) = new a, new b
*)

class ['d] c = object
  constraint 'd = _ #d

  val d : 'd option ref = ref None
  method set_d d' = d := Some d'
  method get_d = match ! d with
    | Some d -> d
    | None -> raise Not_found
  method foo = ()

end

and ['c] d = object
  constraint 'c = _ #c

  val c : 'c option ref = ref None
  method set_c c' = c := Some c'
  method get_c = match ! c with
    | Some c -> c
    | None -> raise Not_found

end

let _check _ : 'd c * ('d c d as 'd) = new c, new d

class ['f] e = object
  constraint 'f = _ #f

  val f : 'f option ref = ref None
  method set_f f' = f := Some f'
  method get_f = match ! f with
    | Some f -> f
    | None -> raise Not_found

end

and ['e] f = object
  constraint 'e = _ #e

  val e : 'e option ref = ref None
  method set_e e' = e := Some e'
  method get_e = match ! e with
    | Some e -> e
    | None -> raise Not_found

end

let _check _ : 'f e * ('f e f as 'f) = new e, new f

class ['y] x = object
  inherit ['y] a
  constraint 'y = (_, _) #y
end

and ['x, 'z] y = object
  inherit ['x] b
  inherit ['z] c
  constraint 'x = _ #x
  constraint 'z = _ #z
end

and ['y] z = object
  inherit ['y] d
  constraint 'y = (_, _) #y
end

and ['y, 'z2] z1 = object
  inherit ['y] z
  inherit ['z2] e
end

and ['y, 'z1] z2 = object
  inherit ['y] z
  inherit ['z1] f
end

let _check _ : 'y x * (('y x, 'y z) y as 'y) * ('y, 'z2) z1 * (('y, ('y, 
'z2) z1) z2 as 'z2) = new x, new y, new z1, new z2

let x = new x
and y = new y
and z1 = new z1
and z2 = new z2

let _ =
  x#set_b y;
  y#set_a x;
  y#set_d (z1 :> _ z);
  z1#set_c y;
  y#set_d (z2 :> _ z);
  z2#set_c y;
  z1#set_f z2;
  z2#set_e z1

Here, the checks are becomming hard to write however. Note that both the 
type and implementation of foo were unspecified in b, and this was later 
fixed by inheriting from c.

In a real use, each of the successive extensions would probably go in a 
dedicated file. I hope that this will allow me to modularize my code 
some day :-).

Tiphaine Turpin


[-- Attachment #2: Type: text/html, Size: 13911 bytes --]

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

* Re: [Caml-list] OO programming
  2008-02-28  8:34       ` Keiko Nakata
  2008-02-28 13:30         ` Andrej Bauer
@ 2008-02-29 14:35         ` Tiphaine.Turpin
  2008-02-29 15:58           ` Keiko Nakata
  1 sibling, 1 reply; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-29 14:35 UTC (permalink / raw)
  To: Keiko Nakata; +Cc: garrigue, caml-list

Keiko Nakata a écrit :
> Hello,
>
>   
>>> As for extension, I'm fully satisfied. But the verbosity level is 
>>> annoying for scalability...
>>>       
>> Well, yes, that's always the problem with functors...
>>     
>
> Since there are some people (including me) 
> who are interested in using functors and recursive modules 
> in the style of object-oriented context, 
>   
Do you mean including classes in functors, as Jacques Garrigue 
described, or using modules like classes ?

> I thought that it could be useful to devise 
> a (camlp4) syntax extension which mitigates this a bit painful verbosity.
camlp4 extensions may help. I already used some for objects (related to 
initializers), and I plan to investigate it further, possibly borrowing 
code from Jacques Garrigue. In the context of functors, the problem is 
that a lot of code would probably remain specific and still need to be 
written by hand, for example, the row types for classes...

Tiphaine Turpin
>  
>
> At the moment, I have no idea which syntax is general enough and intuitive for us,
> but as far as I understand we always follow similar encodings. 
>
> With best regards,
> Keiko
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>   


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

* Re: [Caml-list] OO programming
  2008-02-29 14:35         ` Tiphaine.Turpin
@ 2008-02-29 15:58           ` Keiko Nakata
  2008-03-03  9:40             ` Tiphaine.Turpin
  0 siblings, 1 reply; 32+ messages in thread
From: Keiko Nakata @ 2008-02-29 15:58 UTC (permalink / raw)
  To: caml-list

> > Since there are some people (including me) 
> > who are interested in using functors and recursive modules 
> > in the style of object-oriented context, 
> >   
> Do you mean including classes in functors, as Jacques Garrigue 
> described, or using modules like classes ?

The former; to include classes in functors. 

> > I thought that it could be useful to devise 
> > a (camlp4) syntax extension which mitigates this a bit painful verbosity.
> camlp4 extensions may help. I already used some for objects (related to 
> initializers), and I plan to investigate it further, possibly borrowing 
> code from Jacques Garrigue. In the context of functors, the problem is 
> that a lot of code would probably remain specific and still need to be 
> written by hand, for example, the row types for classes...

As I see Jacques's code, he gradually extends the module type S 
to S' and S''. Type declarations in module types and type definitions
in structures involving types event, observer and subject are duplicated
everywhere with slight modifications. 
Why can we not extend the previously defined module type 
in a less verbose way?

We may still need to write row types by hand. 
But I think we should (ideally) do it in a extensible way without duplications. 

With best regards,
Keiko


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

* Re: [Caml-list] OO programming
  2008-02-29 15:58           ` Keiko Nakata
@ 2008-03-03  9:40             ` Tiphaine.Turpin
  2008-03-03 10:20               ` Jacques Garrigue
  2008-03-03 15:18               ` Keiko Nakata
  0 siblings, 2 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-03-03  9:40 UTC (permalink / raw)
  To: Keiko Nakata; +Cc: caml-list

Keiko Nakata a écrit :
>>> Since there are some people (including me) 
>>> who are interested in using functors and recursive modules 
>>> in the style of object-oriented context, 
>>>   
>>>       
>> Do you mean including classes in functors, as Jacques Garrigue 
>> described, or using modules like classes ?
>>     
>
> The former; to include classes in functors. 
>
>   
>>> I thought that it could be useful to devise 
>>> a (camlp4) syntax extension which mitigates this a bit painful verbosity.
>>>       
>> camlp4 extensions may help. I already used some for objects (related to 
>> initializers), and I plan to investigate it further, possibly borrowing 
>> code from Jacques Garrigue. In the context of functors, the problem is 
>> that a lot of code would probably remain specific and still need to be 
>> written by hand, for example, the row types for classes...
>>     
>
> As I see Jacques's code, he gradually extends the module type S 
> to S' and S''. Type declarations in module types and type definitions
> in structures involving types event, observer and subject are duplicated
> everywhere with slight modifications. 
> Why can we not extend the previously defined module type 
> in a less verbose way?
>   
I agree. Maybe the idea of using parametric class type definitions (in 
WRichT) and defining unparameterized types afterwards (in S'') could be 
helpfull, as such definitions can be extended with the "inherit 
<class-type>" construct. Otherwise you would need to keep syntaxically 
the successive declarations with camlp4 for future use, which is not 
very handy. I don't know if there is an equivalent to inherit for 
polymorphic variants: "type t' = private  [> t | ...] doesn't seem to 
work. And using objects to encode variants is possible, but you may be 
used to more serious solutions...

Tiphaine Turpin
> We may still need to write row types by hand. 
> But I think we should (ideally) do it in a extensible way without duplications. 
>
> With best regards,
> Keiko
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>   


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

* Re: [Caml-list] OO programming
  2008-03-03  9:40             ` Tiphaine.Turpin
@ 2008-03-03 10:20               ` Jacques Garrigue
  2008-03-03 10:30                 ` Tiphaine.Turpin
  2008-03-03 15:18               ` Keiko Nakata
  1 sibling, 1 reply; 32+ messages in thread
From: Jacques Garrigue @ 2008-03-03 10:20 UTC (permalink / raw)
  To: Tiphaine.Turpin; +Cc: keiko, caml-list

From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>

> I don't know if there is an equivalent to inherit for 
> polymorphic variants: "type t' = private  [> t | ...] doesn't seem to 
> work. And using objects to encode variants is possible, but you may be 
> used to more serious solutions...

It's actually simple with polymorphic variants than with objects.
You just write
    type t' = private [> t ]
but t has to be a non-private polymorphic variant type.
So you have to maintain a hierarchy of non-private types, to be
able to build your private types. This should be already apparent in
my code for event.

Jacques


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

* Re: [Caml-list] OO programming
  2008-03-03 10:20               ` Jacques Garrigue
@ 2008-03-03 10:30                 ` Tiphaine.Turpin
  0 siblings, 0 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-03-03 10:30 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: caml-list

Jacques Garrigue a écrit :
> From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>
>
>   
>> I don't know if there is an equivalent to inherit for 
>> polymorphic variants: "type t' = private  [> t | ...] doesn't seem to 
>> work. And using objects to encode variants is possible, but you may be 
>> used to more serious solutions...
>>     
>
> It's actually simple with polymorphic variants than with objects.
> You just write
>     type t' = private [> t ]
> but t has to be a non-private polymorphic variant type.
> So you have to maintain a hierarchy of non-private types, to be
> able to build your private types. This should be already apparent in
> my code for event.
>   
Yes, indeed. So, no need for another bad hack :-).
> Jacques
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>   


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

* Re: [Caml-list] OO programming
  2008-03-03  9:40             ` Tiphaine.Turpin
  2008-03-03 10:20               ` Jacques Garrigue
@ 2008-03-03 15:18               ` Keiko Nakata
  2008-03-03 19:25                 ` Tiphaine Turpin
  1 sibling, 1 reply; 32+ messages in thread
From: Keiko Nakata @ 2008-03-03 15:18 UTC (permalink / raw)
  To: caml-list

> > As I see Jacques's code, he gradually extends the module type S 
> > to S' and S''. Type declarations in module types and type definitions
> > in structures involving types event, observer and subject are duplicated
> > everywhere with slight modifications. 
> > Why can we not extend the previously defined module type 
> > in a less verbose way?
> >   
> I agree. Maybe the idea of using parametric class type definitions (in 
> WRichT) and defining unparameterized types afterwards (in S'') could be 
> helpfull, as such definitions can be extended with the "inherit 
> <class-type>" construct. Otherwise you would need to keep syntaxically 
> the successive declarations with camlp4 for future use, which is not 
> very handy. 

Parametric class type definitions should be helpful.
We might need as many type parameters as (class) type definitions involved;
do you think this can be problematic, 
particularly in respect of type error messages?

Hopefully we want to start with S and 
derive its refinements by extending S bit by bit. 
But here is one problem: module type declarations (e.g. S) and 
structures (e.g. WindowA) are completely different entities;
it can be handy in practice if we can include S in WihdowA 
and refine the included types by giving concrete representations
to abstract types (e.g. event).

Well, I know that module types and structures are 
indeed completely different 
from the theoretical point of view....

Here is another thing that may be worth attention.
The types observer and subject in WRichT are not recursive, 
but we take their fix-point in S''. 
Unfortunately we cannot do this kind of refinement 
via the "with" construct. 

Anyway, I think we are almost exactly following OO-programming style
in a more explicit thus more verbose way. 
So I conjecture that if we come up with good syntactic sugar, 
we can be as concise as OOP; as you suggest it may well be 
a good idea to expand the sugar into parametric class definitions, 
possibly combined with functors and private row types to increase modularity. 

Best, 
Keiko


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

* Re: [Caml-list] OO programming
  2008-03-03 15:18               ` Keiko Nakata
@ 2008-03-03 19:25                 ` Tiphaine Turpin
  2008-03-04 14:00                   ` Keiko Nakata
  0 siblings, 1 reply; 32+ messages in thread
From: Tiphaine Turpin @ 2008-03-03 19:25 UTC (permalink / raw)
  To: Keiko Nakata; +Cc: caml-list

Keiko Nakata a écrit :
>>> As I see Jacques's code, he gradually extends the module type S 
>>> to S' and S''. Type declarations in module types and type definitions
>>> in structures involving types event, observer and subject are duplicated
>>> everywhere with slight modifications. 
>>> Why can we not extend the previously defined module type 
>>> in a less verbose way?
>>>   
>>>       
>> I agree. Maybe the idea of using parametric class type definitions (in 
>> WRichT) and defining unparameterized types afterwards (in S'') could be 
>> helpfull, as such definitions can be extended with the "inherit 
>> <class-type>" construct. Otherwise you would need to keep syntaxically 
>> the successive declarations with camlp4 for future use, which is not 
>> very handy. 
>>     
>
> Parametric class type definitions should be helpful.
> We might need as many type parameters as (class) type definitions involved;
> do you think this can be problematic, 
> particularly in respect of type error messages?
>   
Only experiments can tell us. But I suspect that using a systematic 
scheme for defining classes and relating them to each other should avoid 
users to make too many errors that come from a misunderstanding of the 
type system ('self escaping its scoope, or unified wit a closed type, 
etc.), thus allowing "advanced" use of caml objects by non type systems 
experts (including me).
> Hopefully we want to start with S and 
> derive its refinements by extending S bit by bit. 
> But here is one problem: module type declarations (e.g. S) and 
> structures (e.g. WindowA) are completely different entities;
> it can be handy in practice if we can include S in WihdowA 
> and refine the included types by giving concrete representations
> to abstract types (e.g. event).
>
> Well, I know that module types and structures are 
> indeed completely different 
> from the theoretical point of view....
>   
In this case where they are only made of (the same) types with a class 
type on one side and a private row type on the other side, this 
theoretical difference is far from obvious in practise, and this is one 
of the reasons why such code is so hard to read (as the same thing seem 
to be repeated twice). This would be nice if you could write your types 
once (possibly in a very small subset of the type language) and have the 
module and the module type be generated from the same code.
> Here is another thing that may be worth attention.
> The types observer and subject in WRichT are not recursive, 
> but we take their fix-point in S''. 
> Unfortunately we cannot do this kind of refinement 
> via the "with" construct. 
>
> Anyway, I think we are almost exactly following OO-programming style
> in a more explicit thus more verbose way. 
> So I conjecture that if we come up with good syntactic sugar, 
> we can be as concise as OOP; as you suggest it may well be 
> a good idea to expand the sugar into parametric class definitions, 
> possibly combined with functors and private row types to increase modularity. 
>   
I plan to do some reasonable scale "OOcaml" coding (in my spare time) 
for some project. I will first see if I can use some systematic scheme 
successfully before I try anything with camlp4. That said, some of us 
tend to think of everything only from within ocaml, and I know that some 
day I should give a try to other systems, like Scala and its "traits".

Tiphaine Turpin

> Best, 
> Keiko
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
>   


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

* Re: [Caml-list] OO programming
  2008-03-03 19:25                 ` Tiphaine Turpin
@ 2008-03-04 14:00                   ` Keiko Nakata
  0 siblings, 0 replies; 32+ messages in thread
From: Keiko Nakata @ 2008-03-04 14:00 UTC (permalink / raw)
  To: caml-list

Hello.

> > Parametric class type definitions should be helpful.
> > We might need as many type parameters as (class) type definitions involved;
> > do you think this can be problematic, 
> > particularly in respect of type error messages?
> >   
> Only experiments can tell us. But I suspect that using a systematic 
> scheme for defining classes and relating them to each other should avoid 
> users to make too many errors that come from a misunderstanding of the 
> type system ('self escaping its scoope, or unified wit a closed type, 
> etc.), thus allowing "advanced" use of caml objects by non type systems 
> experts (including me).

Boilerplates that help us avoid typing errors...
That sounds nice. 

> I plan to do some reasonable scale "OOcaml" coding (in my spare time) 
> for some project. I will first see if I can use some systematic scheme 
> successfully before I try anything with camlp4. 

I also look for how I can minimize in a (hopefully) intuitive way 
Jacques's code, avoiding bizarre code duplication. 
Please let me know when you have good news. 

> That said, some of us 
> tend to think of everything only from within ocaml, and I know that some 
> day I should give a try to other systems, like Scala and its "traits".

I am sure you can enjoy exotic time if you try to exploit Scala's goodies :-)
In respect of the exact subject we have been discussing, Scala may be more adapted.

As far as I am concerned, fortunately(?), polymorphic variants and type inference 
and other many many goodies of OCaml 
keep me from turning to another language. 

Best,
Keiko




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

end of thread, other threads:[~2008-03-04 14:00 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-21  9:31 OO programming Tiphaine Turpin
2008-02-21  9:42 ` [Caml-list] " Erik de Castro Lopo
2008-02-21 13:38 ` Remi Vanicat
2008-02-24 16:33 ` [Caml-list] " Dirk Thierbach
2008-02-25  9:23   ` Tiphaine.Turpin
2008-02-25 15:48     ` Edgar Friendly
2008-02-25 16:02       ` Berke Durak
2008-02-25 20:12         ` Dirk Thierbach
2008-02-25 20:51           ` Tiphaine.Turpin
2008-02-25 23:03             ` Dirk Thierbach
2008-02-25 20:10     ` Dirk Thierbach
2008-02-25 21:49       ` Tiphaine.Turpin
2008-02-25 23:07         ` Dirk Thierbach
2008-02-29 14:22       ` Tiphaine.Turpin
2008-02-26  6:17 ` Jacques Garrigue
2008-02-26  9:36   ` Julien Signoles
2008-02-27  0:25   ` Tiphaine.Turpin
2008-02-27  1:37     ` Jacques Garrigue
2008-02-28  8:34       ` Keiko Nakata
2008-02-28 13:30         ` Andrej Bauer
2008-02-28 15:18           ` Keiko Nakata
2008-02-28 16:02           ` Edgar Friendly
2008-02-29 14:35         ` Tiphaine.Turpin
2008-02-29 15:58           ` Keiko Nakata
2008-03-03  9:40             ` Tiphaine.Turpin
2008-03-03 10:20               ` Jacques Garrigue
2008-03-03 10:30                 ` Tiphaine.Turpin
2008-03-03 15:18               ` Keiko Nakata
2008-03-03 19:25                 ` Tiphaine Turpin
2008-03-04 14:00                   ` Keiko Nakata
2008-02-27  7:40     ` Dirk Thierbach
2008-02-27 14:04       ` Tiphaine.Turpin

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