Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[Caml-list] Smells like duck-typing

10 views
Skip to first unread message

Dario Teixeira

unread,
Oct 17, 2007, 9:36:22 AM10/17/07
to caml...@yquem.inria.fr
Hi,

I have been trying to reach a sane modelling in OCaml for a "story"
data structure in a CMS. The problem is that I find myself needing
a degree of expressiveness that I can't find in the language! I do
have a working, tentative solution, but it has a few ugly aspects
that I would very much like to improve. Details follow. (Sorry
for the long post; at least I hope it's not too dense and hard to
follow).

A "full" story record is defined like this:

type full_t =
{
id: int;
title: string;
intro: string;
body: string;
}

(in reality there are other fields, but I'll ommit them for the sake
of clarity). In addition, stories can also come in "blurb" and "fresh"
types, which are essentially (non-disjoint) subsets of the type above:

type blurb_t = type fresh_t =
{ {
id: int; title: string;
title: string; intro: string;
intro: string; body: string;
} }


At last, I have a function "print_metadata" that takes as parameter
either a "full" or a "blurb" story, printing its id and title:

let print_metadata s =
Printf.printf "%d: %s\n" s.id s.title

Now, I have been looking for the best way to model this situation
in OCaml. Here are some options:

a) Use record types, as shown above. However, to avoid namespace clashes,
this would entail putting each record in its own module (neat) or at
least salting each field name (ugly). Suppose that I opt for the former
option and create the modules Full, Blurb, and Fresh, each with a type t:

type story_t = [`Full of Full.t | `Blurb of Blurb.t | `Fresh of Fresh.t]

Note that I have chosen a polymorphic variant because print_metadata only
makes sense for Full.t and Blurb.t types. However, this solution means
there can't be any code sharing between the two branchings, which is just
ridiculous considering they are essentially identical: (and in the real
world, print_metadata is a much bigger function).

let print_metadata = function
| `Full s -> Printf.printf "%d: %s\n" s.Full.id s.Full.title
| `Blurb s -> Printf.printf "%d: %s\n" s.Blurb.id s.Blurb.title

b) Use only full_t and make all fields option types. However, not only is
this cumbersome to use, but is also conceptually wrong, because it does
not capture the fact that, for example, all "blurb" stories have three
*mandatory* fields.

c) Actually put the "Objective" part of OCaml to use. This is the solution
I am using at the moment. This is what it looks like:

class story (id, title, intro, body) =
object
val id: int option = id
val title: string option = title
val intro: string option = intro
val body: string option = body

method id =
match id with
| Some thing -> thing
| None -> failwith "oops"

method title =
match title with
| Some thing -> thing
| None -> failwith "oops"

method intro =
match intro with
| Some thing -> thing
| None -> failwith "oops"

method body =
match body with
| Some thing -> thing
| None -> failwith "oops"
end


class full (id, title, intro, body) =
object
inherit story (Some id, Some title, Some intro, Some body)
end

class blurb (id, title, intro) =
object
inherit story (Some id, Some title, Some intro, None)
end

class fresh (title, intro, body) =
object
inherit story (None, Some title, Some intro, Some body)
end


let print_metadata s =
Printf.printf "%d: %s\n" s#id s#title

This last solution has two big advantages: it provides a relatively
clean interface to users of the module, and allows for code reuse
without duplication. Thanks to the way the object system in OCaml
works, the print_metadata function can operate on any objects that
have the #id and #title methods. It feels almost like the duck-typing
present in languages such as Python (though different, of course).

However, I'm still not completely happy with it, mostly because the
hackery with the optional types inside the story class is ugly. Does
someone have any clever ideas on how this could be modelled/improved?

Thanks,
Dario

___________________________________________________________
Yahoo! Answers - Got a question? Someone out there knows the answer. Try it
now.
http://uk.answers.yahoo.com/

_______________________________________________
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

Daniel Bünzli

unread,
Oct 17, 2007, 10:25:51 AM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr
Use a record with a phantom type (the type must be abstract for this
to work). The record has the common properties as fields and a data
field which is either a list of tagged values (if many kinds of
stories share the same fields) or a variant for the data specific to
a kind of story. If there are operations that are allowed only on
some kind of stories constrain the phantom type in the signature to
enforce this statically.

Best,

Daniel

module Story : sig

type 'a t constraint 'a = [< `Full | `Blurb | `Fresh ]

(* Story creators *)
val full : ... -> `Full t
val blurb : ... -> `Blurb t
val fresh : ... -> `Fresh t

val print_meta_data : [< `Full | `Blurb ] t -> unit

end = struct

type 'a t = {


id : int;
title : string;
intro : string;

data : ...
}
...
end

Chris King

unread,
Oct 17, 2007, 10:34:08 AM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr

Why not have different object types for each of the story types? e.g.

type full = < id: int; title: string; intro: string; body: string >
type blurb = < id: int; title: string; intro: string >
type fresh = < title: string; intro: string; body: string >

print_metadata can remain as is in your object example. In order to
allow functions that operate differently depending on which story type
they're given, use a polymorphic variant like in your first example.
If you wanted print_metadata to take such a type, it could be written
as:

let print_metadata s =
let s' =
match s with
| `Full f -> (f :> < id: int; title: string >)
| `Blurb f -> (f :> < id: int; title: string >)
in
Printf.printf "%d: %s\n" s'#id s'#title

(Note the match gook is unfortunately needed to get the typing right...)

If on the other hand you wanted to ditch objects entirely, you could
do a similar thing using modules and functors. E.g.:

module Full = struct
type t = { id: int; title: string; intro: string; body: string }
let id s = s.id
let title s = s.title
let intro s = s.intro
let body s = s.body
end

(* etc. *)

module Print_metadata(S: sig type t val id: t -> string val title: t
-> string) = struct
let f s = Printf.printf "%d: %s\n" (S.id s) (S.title s)
end

Of course, since calls to print_metadata now look like
"Print_metadata(Full).f story", you're essentially forced to write out
the types of everything, which is probably what you wanted to avoid
anyway.

HTH,
Chris

Dario Teixeira

unread,
Oct 17, 2007, 10:48:10 AM10/17/07
to caml...@yquem.inria.fr
> I think your solution is to fully use the "Objective" part of OCaml,
> that is, using subtyping (technically it's polymorphic rows typing) :

Hi,

Yes, you're right. Doing away with the inheritance -- while having the
disadvantage of forcing the redeclaration of all common fields -- does
allow me to throw away the ugliness of the option types. It is in the
end the lesser of two evils...

Cheers,
Dario

___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

Dario Teixeira

unread,
Oct 17, 2007, 10:59:36 AM10/17/07
to Chris King, caml...@yquem.inria.fr
Hi,

> Why not have different object types for each of the story types? e.g.

Yes, other people have suggested the same, and while not a perfect
solution (because of the reduplication of fields), the object subtyping
feature of OCaml (the bit that smells a lot like duck-typing...)
is powerful enough to avoid the code duplication in functions such
as print_metadata. More important, it avoids the ugly hackery with
option types.


> If on the other hand you wanted to ditch objects entirely, you could
> do a similar thing using modules and functors. E.g.:

That is actually an interesting approach. If I'm not mistaken, the
semantics for the parameter to a functor is "a module whose signature
contains at least this", which is again similar to duck-typing...

Cheers,
Dario

___________________________________________________________
Yahoo! Answers - Got a question? Someone out there knows the answer. Try it
now.
http://uk.answers.yahoo.com/

_______________________________________________

Dario Teixeira

unread,
Oct 17, 2007, 11:02:23 AM10/17/07
to Francisco Jos? Valverde Albacete, caml...@yquem.inria.fr
> To me your problems looks like a good candidate for XML-modelling and
> reusing all the tools for working in XML. The more so because XML was
> made... exactly for that! Why don't you give it at least a somewhat
> serious thought?

Hi,

You mean like CDuce and/or OCamlduce?

skaller

unread,
Oct 17, 2007, 11:04:07 AM10/17/07
to Daniel Bünzli, caml...@yquem.inria.fr

On Wed, 2007-10-17 at 16:25 +0200, Daniel Bünzli wrote:
> Use a record with a phantom type (the type must be abstract for this
> to work).

I would use something weaker, and I wouldn't use objects
either. To me, this is essentially a dynamic scenario,
so the data type should be dynamic. For example:

type field_t = [
| `Title of string
| `Intro of string
| `Body of string
| `Field of string * string
]

type story_t = field_t list


--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

Dario Teixeira

unread,
Oct 17, 2007, 11:14:03 AM10/17/07
to skaller, Daniel Bünzli, caml...@yquem.inria.fr
> I would use something weaker, and I wouldn't use objects
> either. To me, this is essentially a dynamic scenario,
> so the data type should be dynamic. For example:
>
> type field_t = [
> | `Title of string
> | `Intro of string
> | `Body of string
> | `Field of string * string
> ]
>
> type story_t = field_t list
>

Hi,

Interesting idea, but how would you access the fields in a convenient
manner? Note that nothing beats record access (say, story.title) or even
object method call (story#title) in convenience and readability.

(I guess the convenience aspect could be sorted out with a syntax
extension, but then we're moving into far more complex territory
than I hope is necessary...)

Cheers,
Dario


___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

_______________________________________________

Zheng Li

unread,
Oct 17, 2007, 11:24:39 AM10/17/07
to caml...@inria.fr

Hi,

I would vote for the object solution. However, both record and object have
their own pros and cons. The actual solution should depends on your requirement
and taste. Just for an example, the following data structure is a possibility:

# type +'a t = {title:string; intro:string; extra:'a} constraint 'a = < .. >;;
# let fresh (title,intro,(body:string)) =
{title=title; intro=intro; extra=object method body=body end};;
# let blurb ((id:int),title,intro) =
{title=title; intro=intro; extra=object method id=id end};;
# let print_metadata s = Printf.printf "%d: %s\n" s.extra#id s.title;;
val print_metadata : < id : int; .. > t -> unit = <fun>

Dario Teixeira <dariot...@yahoo.com> writes:
> I have been trying to reach a sane modelling in OCaml for a "story"
> data structure in a CMS. The problem is that I find myself needing
> a degree of expressiveness that I can't find in the language! I do
> have a working, tentative solution, but it has a few ugly aspects
> that I would very much like to improve. Details follow. (Sorry
> for the long post; at least I hope it's not too dense and hard to
> follow).

--
Zheng Li
http://www.pps.jussieu.fr/~li

Vincent Aravantinos

unread,
Oct 17, 2007, 11:24:43 AM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr

Le 17 oct. 07 à 15:35, Dario Teixeira a écrit :

> I have been trying to reach a sane modelling in OCaml for a "story"
> data structure in a CMS.

(...)


> Does someone have any clever ideas on how this could be
> modelled/improved?

Also take a look at:

http://tech.groups.yahoo.com/group/ocaml_beginners/message/8654

Cheers,
V.

Arnaud Spiwack

unread,
Oct 17, 2007, 11:26:35 AM10/17/07
to caml...@yquem.inria.fr
First, let us emphasize that this goes down to the original "option"
thing, but in a more dynamic fashion. It is very possible to wrap it
back with phantom types, which would probably be a good idea in this case.

My taste for this sort of thing is to make all this a finite map of some
sort (might be implemented as a list, but the primitive should ensure a
form of unicity, therefore an additional layer of abstract datatype).

Then you would need a type to query over :
"field_field = [ `Title | `Intro | `Body | `Field ].

Implement a function "fetch" that returns you a field_t or raises an
exception (you can do similarily a function "mem" etc).

Then you can define

let title m = let `Title s = fetch `Title m in s;;
and give it type [> `Title ] story -> string (to be sure not to raise
an exception, modulo right phantom type invariants).

That's my own methodology for this sort of types.

Arnaud Spiwack


Dario Teixeira a écrit :

Daniel Bünzli

unread,
Oct 17, 2007, 11:33:06 AM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr, skaller

Le 17 oct. 07 à 17:13, Dario Teixeira a écrit :

>> type field_t = [
>> | `Title of string
>> | `Intro of string
>> | `Body of string
>> | `Field of string * string
>> ]
>>
>> type story_t = field_t list
>>

> Interesting idea,

If this is right for you then what you want is property lists and
these two solutions [1,2] may be of interest to you.

Daniel

[1] http://mlton.org/PropertyList
[2] http://caml.inria.fr/pub/ml-archives/caml-list/
2005/07/1b7a28921aa0a0e002df9e67ace7b60e.fr.html

Chris King

unread,
Oct 17, 2007, 12:22:08 PM10/17/07
to Daniel Bünzli, caml...@yquem.inria.fr, skaller
On 10/17/07, Daniel Bünzli <daniel....@epfl.ch> wrote:
> [2] http://caml.inria.fr/pub/ml-archives/caml-list/2005/07/1b7a28921aa0a0e002df9e67ace7b60e.fr.html

See also Jacques' follow up, where he provides a syntax extension for
these "polymorphic maps" (essentially type-safe property lists):
http://caml.inria.fr/pub/ml-archives/caml-list/2005/07/314a9eb55585d32e7876ef424f5994ae.fr.html

skaller

unread,
Oct 17, 2007, 12:52:53 PM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr, Daniel Bünzli

On Wed, 2007-10-17 at 16:13 +0100, Dario Teixeira wrote:
> > I would use something weaker, and I wouldn't use objects
> > either. To me, this is essentially a dynamic scenario,
> > so the data type should be dynamic. For example:
> >
> > type field_t = [
> > | `Title of string
> > | `Intro of string
> > | `Body of string
> > | `Field of string * string
> > ]
> >
> > type story_t = field_t list
> >
>
> Hi,
>
> Interesting idea, but how would you access the fields in a convenient
> manner?

let rec get_title ls = match ls with
| `Title s :: _ -> s
| _ ::t -> get_title t
| [] -> raise Not_found (* sucks.. *)


--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

_______________________________________________

skaller

unread,
Oct 17, 2007, 12:58:20 PM10/17/07
to Daniel Bünzli, caml...@yquem.inria.fr

On Wed, 2007-10-17 at 17:32 +0200, Daniel Bünzli wrote:
> Le 17 oct. 07 à 17:13, Dario Teixeira a écrit :
>
> >> type field_t = [
> >> | `Title of string
> >> | `Intro of string
> >> | `Body of string
> >> | `Field of string * string
> >> ]
> >>
> >> type story_t = field_t list
> >>
> > Interesting idea,
>
> If this is right for you then what you want is property lists and
> these two solutions [1,2] may be of interest to you.

Yes, my 'solution' is just a hint. Felix actually uses
something similar to the above structure to record properties
of functions, eg 'pure', 'inline', etc.

This kind of data structure is weakly typed, but that's
part of the idea. Someone probably has an actual representation
of BibTeX data, which is similar in spirit.

Perhaps this weak typing is too weak and can be beefed up,
for example if the title is mandatory, a record PLUS
a property list.

--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

_______________________________________________

Robert Fischer

unread,
Oct 17, 2007, 12:59:45 PM10/17/07
to skaller, caml...@yquem.inria.fr, Daniel Bünzli
If you go with this approach, why wouldn't you just use a map?

~~ Robert.

> let rec get_title ls = match ls with
> | `Title s :: _ -> s
> | _ ::t -> get_title t
> | [] -> raise Not_found (* sucks.. *)
>
>
>

_______________________________________________

Richard Jones

unread,
Oct 17, 2007, 4:00:12 PM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr
Sounds a bit like this rather lengthy thread:

http://caml.inria.fr/pub/ml-archives/caml-list/2004/12/a0924032de03d517cb8cb8f2adde6c94.en.html

Rich.

--
Richard Jones
Red Hat

Alain Frisch

unread,
Oct 17, 2007, 4:23:15 PM10/17/07
to Dario Teixeira, caml...@yquem.inria.fr, Francisco Jos? Valverde Albacete
Dario Teixeira wrote:
>> To me your problems looks like a good candidate for XML-modelling and
>> reusing all the tools for working in XML. The more so because XML was
>> made... exactly for that! Why don't you give it at least a somewhat
>> serious thought?
>
> Hi,
>
> You mean like CDuce and/or OCamlduce?

I'm not particularly encouraging the use of OCamlduce here, but FWIW, a
solution would look like:

type title = {{ { title=Latin1 ..} }}
type intro = {{ { intro=Latin1 ..} }}
type blurb_t = {{ { id=Int .. } ++ title ++ intro }}
type fresh_t = {{ { body=Latin1 .. } ++ title ++ intro }}
type full_t = {{ blurb_t & fresh_t }} (* intersection type *)

let print_metadata s =
Printf.printf "%d: %s\n" {:s.id:} {:s.title:}

If you're ok duplicating fields, the type definitions can be simplified:

type blurb_t = {{ {id=Int title=Latin1 intro=Latin1 ..} }}
type fresh_t = {{ {body=Latin1 title=Latin1 intro=Latin1 ..} }}
type full_t = {{ {id=Int body=Latin1 title=Latin1 intro=Latin1 ..} }}


-- Alain

Dario Teixeira

unread,
Oct 17, 2007, 4:24:31 PM10/17/07
to caml...@yquem.inria.fr
Hi,

And a big thanks to everyone -- you've really given me a lot of interesting
ideas. For the time being, I am going for the no-inheritance object solution:
it avoids the option type mess, provides a clean interface to users of the
module, and allows code reuse thanks to OCaml's structural subtyping feature
(the "duck" in this thread's title). Moreover, while obviously not as concise
as a record, the declaration of each story variant is still quite economical:

class full (id, title, intro, body) =
object

method id: int = id
method title: string = title
method intro: string = intro
method body: string = body
end

class blurb (id, title, intro) =
object

method id: int = id
method title: string = title
method intro: string = intro
end


Etc, etc. I can live with this.

Cheers,
Dario

___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

_______________________________________________

Stefano Zacchiroli

unread,
Oct 18, 2007, 3:29:05 AM10/18/07
to caml...@yquem.inria.fr
On Wed, Oct 17, 2007 at 12:21:39PM -0400, Chris King wrote:
> See also Jacques' follow up, where he provides a syntax extension for
> these "polymorphic maps" (essentially type-safe property lists):
> http://caml.inria.fr/pub/ml-archives/caml-list/2005/07/314a9eb55585d32e7876ef424f5994ae.fr.html

Do you know if that syntax extension is still working with ocaml/camlp4
3.10?

Thanks for your pointer, Cheers.

--
Stefano Zacchiroli -*- PhD in Computer Science ............... now what?
zack@{cs.unibo.it,debian.org,bononia.it} -%- http://www.bononia.it/zack/
(15:56:48) Zack: e la demo dema ? /\ All one has to do is hit the
(15:57:15) Bac: no, la demo scema \/ right keys at the right time

Stefano Zacchiroli

unread,
Oct 18, 2007, 3:38:13 AM10/18/07
to caml...@yquem.inria.fr
On Wed, Oct 17, 2007 at 09:24:05PM +0100, Dario Teixeira wrote:
> ideas. For the time being, I am going for the no-inheritance object solution:

Uhm sorry, why aren't you going for the object *with* inheritance
solution? The one you're proposing here is more lightweight than the
initial one due to the lack of option types, not due to the lack of
inheritance.

Can't you just go for:

class blurb (id, title, intro) =
object
method id: int = id
method title: string = title
method intro: string = intro
end

class full (id, title, intro, body) =
object
inherit blurb (id, title, intro)


method body: string = body
end

that way you would also gain code reuse and can maybe define printing
functions on top of each other.

Cheers.

--
Stefano Zacchiroli -*- PhD in Computer Science ............... now what?
zack@{cs.unibo.it,debian.org,bononia.it} -%- http://www.bononia.it/zack/
(15:56:48) Zack: e la demo dema ? /\ All one has to do is hit the
(15:57:15) Bac: no, la demo scema \/ right keys at the right time

_______________________________________________

Jacques Garrigue

unread,
Oct 18, 2007, 4:34:17 AM10/18/07
to za...@bononia.it, caml...@yquem.inria.fr
I've just added versions of pa_oo and pa_polymap working with 3.10.
Sorry for the long delay...

http://www.math.nagoya-u.ac.jp/~garrigue/code/ocaml.html

Jacques Garrigue


From: Stefano Zacchiroli <za...@bononia.it>
> On Wed, Oct 17, 2007 at 12:21:39PM -0400, Chris King wrote:
> > See also Jacques' follow up, where he provides a syntax extension for
> > these "polymorphic maps" (essentially type-safe property lists):
> > http://caml.inria.fr/pub/ml-archives/caml-list/2005/07/314a9eb55585d32e7876ef424f5994ae.fr.html
>
> Do you know if that syntax extension is still working with ocaml/camlp4
> 3.10?

_______________________________________________

Dario Teixeira

unread,
Oct 18, 2007, 6:32:02 AM10/18/07
to Stefano Zacchiroli, caml...@yquem.inria.fr
Hi,

> Uhm sorry, why aren't you going for the object *with* inheritance
> solution? The one you're proposing here is more lightweight than the
> initial one due to the lack of option types, not due to the lack of
> inheritance.
>
> Can't you just go for:
>
> class blurb (id, title, intro) =
> object
> method id: int = id
> method title: string = title
> method intro: string = intro
> end
>
> class full (id, title, intro, body) =
> object
> inherit blurb (id, title, intro)
> method body: string = body
> end

While that would indeed involve a little bit of less typing, it's also
conceptually wrong, because a Full story is not a derivation of a Blurb
with an extra field.

Ideally, I should be able to declare a "platonic" Story that includes
all fields that describe a story. A Full story is just the real-world
clone of the platonic ideal, while a Blurb is a Story with the constraint
that there is no "body" field declared (and so on).

Cheers,
Dario

___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

_______________________________________________

Stefano Zacchiroli

unread,
Oct 18, 2007, 6:38:03 AM10/18/07
to caml...@yquem.inria.fr
On Thu, Oct 18, 2007 at 11:31:34AM +0100, Dario Teixeira wrote:
> > class full (id, title, intro, body) =
> > object
> > inherit blurb (id, title, intro)
> > method body: string = body
> > end
>
> While that would indeed involve a little bit of less typing, it's also
> conceptually wrong, because a Full story is not a derivation of a Blurb
> with an extra field.

Well, remember that in ocaml inheritance is not an instance of "is a"
relationship among classes, but rather "method inclusion" (if you really
want a name for this :)). So you can imagine that blurb (or call it
otherwise) is just a set of methods denoting how a blurb-like class
should behave, and here you have back the duck-typing mentioned in the
subject, and than you "inherit" from it both in the actual blurb class
and in the full class.

Just my 0.02€,
Cheers.

--
Stefano Zacchiroli -*- PhD in Computer Science ............... now what?
zack@{cs.unibo.it,debian.org,bononia.it} -%- http://www.bononia.it/zack/
(15:56:48) Zack: e la demo dema ? /\ All one has to do is hit the
(15:57:15) Bac: no, la demo scema \/ right keys at the right time

_______________________________________________

Robert Fischer

unread,
Oct 18, 2007, 9:29:24 AM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr, Stefano Zacchiroli

> Ideally, I should be able to declare a "platonic" Story that includes
> all fields that describe a story. A Full story is just the real-world
> clone of the platonic ideal, while a Blurb is a Story with the constraint
> that there is no "body" field declared (and so on).
>
That seems backwards from the way OO inheritance is supposed to work.
You don't go from a more feature-rich case to a less feature-rich case
-- it's the other way around.

If you s/blurb/header/g or s/blurb/summary/g, then a Full story arguably
is-a blurb with a body tacked on.

~~ Robert.

Dario Teixeira

unread,
Oct 18, 2007, 10:11:17 AM10/18/07
to Robert Fischer, caml...@yquem.inria.fr, Stefano Zacchiroli
Hi,

> That seems backwards from the way OO inheritance is supposed to work.
> You don't go from a more feature-rich case to a less feature-rich case
> -- it's the other way around.

Of course it is -- that is precisely why inheritance is the wrong
formalism for my problem! What I need is a "reverse inheritance"
formalism, where a fully defined data structure sits at the root,
and whose descendants are PRUNED versions of the parent.

If sound (and I let the theoreticians decide on that), such a
formalism would be an interesting solution to the type of problem
that originated this thread.

Cheers,
Dario

___________________________________________________________
Yahoo! Answers - Got a question? Someone out there knows the answer. Try it
now.
http://uk.answers.yahoo.com/

_______________________________________________

Brian Hurt

unread,
Oct 18, 2007, 10:19:19 AM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr
Dario Teixeira wrote:

>Hi,
>
>
>
>>That seems backwards from the way OO inheritance is supposed to work.
>>You don't go from a more feature-rich case to a less feature-rich case
>>-- it's the other way around.
>>
>>
>
>Of course it is -- that is precisely why inheritance is the wrong
>formalism for my problem! What I need is a "reverse inheritance"
>formalism, where a fully defined data structure sits at the root,
>and whose descendants are PRUNED versions of the parent.
>
>

The problem with this is that it violates one of the assumptions of
typing, that if type A is (also) a type B, than anywhere you can use a
type B, you can also use a type A. This isn't an assumption limited to
object oriented languages. And this isn't true in your example- if type
A is lacking members type B has, then it's possible to write situations
where a "real" type B can be used, but not a type A- just use a field of
type B that type A doesn't have.

I think I'd recommend rethinking your approach to the problem.

Brian

Arnaud Spiwack

unread,
Oct 18, 2007, 10:29:49 AM10/18/07
to caml...@yquem.inria.fr
Brian Hurt a écrit :
Well, what he is suggesting is to be able to derive a supertype A given
a type B. This is not fundamentaly incorrect. However I fail to find any
reasoning where it is made use of, in usual mathematics. So I am,
personnally, a bit puzzled by the suggestion, unable to say if it might
make sense or not (method exclusion is natural in the setting of
incomplete objets or traits, but it doesn't fit the situation very much,
since it usually produces something that requires to be completed).


Arnaud Spiwack

Brian Hurt

unread,
Oct 18, 2007, 10:46:31 AM10/18/07
to Arnaud Spiwack, caml...@yquem.inria.fr
Arnaud Spiwack wrote:

Saying that A is a supertype of B is the equivelent of saying B is a
subtype of A. Same relation, different direction. In OO lingo, how
they say "B is a subtype of A" is that "B inherits from (is a subclass
of) A".

In either case, while all B's are A's, not all A's are B's. What he
wants is a case where not all A's are B's, but because of the magic
mindreading feature of the language (in conjunction with the DWIM
feature of the hardware), you can treat all A's as B's, with the system
magically filling in the missing values and methods. This is what the
mind reading is needed for- to know how to fill in the missing values.

I will note that Ocaml's row-level polymorphism allows you to invent new
supertypes of a given subtype as needed (a real nice feature, IMHO).
But what he's asking for is fundamentally nonsensical.

Brian

Robert Fischer

unread,
Oct 18, 2007, 10:59:24 AM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr, Stefano Zacchiroli

Arnaud Spiwack

unread,
Oct 18, 2007, 11:03:01 AM10/18/07
to caml...@yquem.inria.fr

>
> Saying that A is a supertype of B is the equivelent of saying B is a
> subtype of A. Same relation, different direction. In OO lingo, how
> they say "B is a subtype of A" is that "B inherits from (is a subclass
> of) A".
That's not true. That's how you say it in *some* Object Oriented
languages. Inheritance is a tool which allows you to derive another
object from another one. You like it to be a subtype though, it usually
is rather difficult to build a sound inheritance system where it can
break subtyping anyway (though if you implement the self-type
specialization in the inheritance but not in the subtyping, it might
work, but it's unlikely to be what you want to do). Thus, let's say that
"inheritance is one way to obtain a subtype B out of a type A" (the only
way in languages such as Java). In OCaml for instance, object types
exist and subtyping between them. Classes are a mere way to produce
values of these types, and to provide an inheritance mechanism (which is
compatible with subtyping).

>
> I will note that Ocaml's row-level polymorphism allows you to invent
> new supertypes of a given subtype as needed (a real nice feature,
> IMHO). But what he's asking for is fundamentally nonsensical.
You don't invent them, they exist. I can't agree to dismiss this
suggestion without further thought. It might end up being non-sensical,
maybe not. It merely suggests a way, to derive new values, whose type
happen to be a supertype of the original one. Why not ? It may be
considered to the Haskell-like "derive" feature, and such. It is just a
way to build new values out of known one.


Arnaud Spiwack

Robert Fischer

unread,
Oct 18, 2007, 11:07:54 AM10/18/07
to Arnaud Spiwack, caml...@yquem.inria.fr

> You don't invent them, they exist. I can't agree to dismiss this
> suggestion without further thought. It might end up being
> non-sensical, maybe not. It merely suggests a way, to derive new
> values, whose type happen to be a supertype of the original one. Why
> not ? It may be considered to the Haskell-like "derive" feature, and
> such. It is just a way to build new values out of known one.
>
Formally, can you explain what you're looking for here, and how it's not
congruent with inheritance?

~~ Robert.

William D. Neumann

unread,
Oct 18, 2007, 11:11:28 AM10/18/07
to Dario Teixeira, Robert Fischer, caml...@yquem.inria.fr, Stefano Zacchiroli
On Thu, 18 Oct 2007 09:58:50 -0500, Robert Fischer wrote

> If you think that a full story as a story with a summary/header,
> and also a body, then you're conceptually into inheritance.
>
> From a formal standpoint, you're saying that all full stories
> can be treated as headers/summaries/"blurbs", but not all
> headers/summary/"blurbs" can be treated as full stories. 
> This is equivalent to saying that full stories are a subtype
> of header/summaries/"blurbs".

Well, I think the problem here is that the mental model is reversed with
respect to the functional model. Mentally, blurbs are kinds of stories --
they are distinguished by their lack of a body. Using the standard
inheritance lingo, however, stories are a kind of blurb -- distinguished by
the inclusion of a body. Really, it seems like we've got a six of one/half-
dozen of the other situation...

--

William D. Neumann

Arnaud Spiwack

unread,
Oct 18, 2007, 11:14:55 AM10/18/07
to caml...@yquem.inria.fr
Robert Fischer a écrit :

>
>> You don't invent them, they exist. I can't agree to dismiss this
>> suggestion without further thought. It might end up being
>> non-sensical, maybe not. It merely suggests a way, to derive new
>> values, whose type happen to be a supertype of the original one. Why
>> not ? It may be considered to the Haskell-like "derive" feature, and
>> such. It is just a way to build new values out of known one.
>>
> Formally, can you explain what you're looking for here, and how it's
> not congruent with inheritance?
>
> ~~ Robert.
Nope. Absolutely not :p . I wouldn't have a clue of what it should
formally look like, since again I can't find any example of mathematical
reasoning using that, I have no intuition at all. But it's not
impossible that there is a good way to make/read such a feature, that's
just what I am stating.


Arnaud Spiwack

Vincent Aravantinos

unread,
Oct 18, 2007, 11:42:46 AM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr

Le 17 oct. 07 à 15:35, Dario Teixeira a écrit :

> Hi,


>
> I have been trying to reach a sane modelling in OCaml for a "story"
> data structure in a CMS.

(...)

> b) Use only full_t and make all fields option types. However, not
> only is
> this cumbersome to use, but is also conceptually wrong, because
> it does
> not capture the fact that, for example, all "blurb" stories have
> three
> *mandatory* fields.

The following uses polymorphic variants for options so as to make
those fields mandatory (thus it is more safe).

However it remains very cumbersome :)) but I found it interesting
(and quite funny).

And to follow the current topic, this solution makes inheritance the
right way (or, at least, the way you want).

But honestly, I don't think it's usable (?)


--------------
(* TYPES *)

type ('a,'b) polyoption = [<`Some of 'a|`None] as 'b

type 'a string_option = (string,[<`None|`Some of string] as 'a)
polyoption
type 'a int_option = (int,[<`None|`Some of int] as 'a) polyoption

type some_string = [`Some of string]
type some_int = [`Some of int]
type nothing = [`None]

type ('a,'b,'c,'d) t = {
id: 'a int_option;
title: 'b string_option;
intro: 'c string_option;
body: 'd string_option
}

(* The types we will finally use *)
type full_t = (some_int, some_string, some_string, some_string) t
type blurp_t = (some_int, some_string, some_string, nothing ) t
type fresh_t = (nothing , some_string, some_string, some_string) t


(* SOME TESTS *)
let full : full_t = {id = `Some 1; title = `Some "blablax"; intro =
`Some "blox" ; body = `Some "pouetx"}

(* Check with a missing field:
# let badfull : full_t = {id = `None; title = `Some "blablax";
intro = `Some "blox" ; body = `Some "pouetx"};;
This expression has type (...)
*)

let blurp : blurp_t = {id = `Some 2; title = `Some "blablay"; intro =
`Some "bloy" ; body = `None}
(* Check with too much fields:
# let badblurp : blurp_t = {id = `Some 2; title = `Some "blablay";
intro = `Some "bloy" ; body = `Some "pouety"};;
This expression has type (...)
*)

let fresh : fresh_t = {id = `None; title = `Some "blablaz"; intro =
`Some "bloz" ; body = `Some "pouetz"}

(* FUNCTIONS *)

let get_body : ('a,'b,'c,'d) t -> string = fun x -> let `Some x =
x.body in x
let get_title : ('a,'b,'c,'d) t -> string = fun x -> let `Some x =
x.title in x;;

------------------


TESTS:

# get_title full;;
- : string = "blablax"
# get_title blurp;;
- : string = "blablay"
# get_title fresh;;
- : string = "blablaz"
# get_body full;;
- : string = "pouetx"
# get_body blurp;;
This expression has type (...)
# get_body fresh;;
- : string = "pouetz"


Isn't it funny ?

Cheers
Vincent

Loup Vaillant

unread,
Oct 18, 2007, 11:48:19 AM10/18/07
to William D. Neumann, caml...@yquem.inria.fr, Stefano Zacchiroli
2007/10/18, William D. Neumann <wneu...@cs.unm.edu>:

> On Thu, 18 Oct 2007 09:58:50 -0500, Robert Fischer wrote
>
> > If you think that a full story as a story with a summary/header,
> > and also a body, then you're conceptually into inheritance.
> >
> > From a formal standpoint, you're saying that all full stories
> > can be treated as headers/summaries/"blurbs", but not all
> > headers/summary/"blurbs" can be treated as full stories.
> > This is equivalent to saying that full stories are a subtype
> > of header/summaries/"blurbs".
>
> Well, I think the problem here is that the mental model is reversed with
> respect to the functional model. Mentally, blurbs are kinds of stories --
> they are distinguished by their lack of a body. Using the standard
> inheritance lingo, however, stories are a kind of blurb -- distinguished by
> the inclusion of a body. Really, it seems like we've got a six of one/half-
> dozen of the other situation...

It feels like the problem is the keyword "inherit". It suggest
C++/Java subtyping, while we just want code reuse. If having stories
inheriting from one another eventually result in less code, I would
consider that cleaner, no matter what "inherit" is supposed to mean.

Sometimes, a tool can be good at something it has not be designed for.
I think this might be the case, here.

Regards,
Loup Vaillant

William D. Neumann

unread,
Oct 18, 2007, 12:08:17 PM10/18/07
to Loup Vaillant, caml...@yquem.inria.fr, Stefano Zacchiroli
On Thu, 18 Oct 2007 17:47:45 +0200, Loup Vaillant wrote

> It feels like the problem is the keyword "inherit". It suggest
> C++/Java subtyping, while we just want code reuse. If having stories
> inheriting from one another eventually result in less code, I would
> consider that cleaner, no matter what "inherit" is supposed to mean.
>
> Sometimes, a tool can be good at something it has not be designed
> for. I think this might be the case, here.

Oh, I absolutely agree there. And there are a couple of important snippets
of the manual that seem to apply:

"Note that the relation between object, class and type in Objective Caml is
very different from that in main stream object-oriented languages like Java
or C++, so that you should not assume that similar keywords mean the same
thing." and "Inheritance is a syntactic relation between classes..."


--

William D. Neumann

Zheng Li

unread,
Oct 18, 2007, 12:11:45 PM10/18/07
to caml...@inria.fr

Yet another.

------------------------------------------------------------------------------
class type blurb =
object method id: int method title:string method intro:string end

class type fresh =
object method title:string method intro:string method body:string end

class type full = object inherit blurb inherit fresh end

class virtual templt = object
val virtual id: int
val virtual title: string
val virtual intro: string
val virtual body: string
method id = id
method title = title
method intro = intro
method body = body
end

let blurb (i, t, it) :> blurb =
object inherit templt val id=i val title=t val intro=it end

let fresh (t, it, b) :> fresh =
object inherit templt val title=t val intro=it val body=b end
------------------------------------------------------------------------------

Strangely though, there seems to be a bug in the OO type system: (The solution
proposed above is safe, as it does coercion)

# let coredump = object inherit templt end;;
val coredump : templt = <obj>
# coredump#title;;

Process caml-toplevel segmentation fault


Zheng Li <l...@pps.jussieu.fr> writes:
> Hi,
>
> I would vote for the object solution. However, both record and object have
> their own pros and cons. The actual solution should depends on your requirement
> and taste. Just for an example, the following data structure is a possibility:
>
> # type +'a t = {title:string; intro:string; extra:'a} constraint 'a = < .. >;;
> # let fresh (title,intro,(body:string)) =
> {title=title; intro=intro; extra=object method body=body end};;
> # let blurb ((id:int),title,intro) =
> {title=title; intro=intro; extra=object method id=id end};;
> # let print_metadata s = Printf.printf "%d: %s\n" s.extra#id s.title;;
> val print_metadata : < id : int; .. > t -> unit = <fun>


>
> Dario Teixeira <dariot...@yahoo.com> writes:
>> I have been trying to reach a sane modelling in OCaml for a "story"

>> data structure in a CMS. The problem is that I find myself needing
>> a degree of expressiveness that I can't find in the language! I do
>> have a working, tentative solution, but it has a few ugly aspects
>> that I would very much like to improve. Details follow. (Sorry
>> for the long post; at least I hope it's not too dense and hard to
>> follow).
>
> --
> Zheng Li
> http://www.pps.jussieu.fr/~li


>
> _______________________________________________
> 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
>

--
Zheng Li
http://www.pps.jussieu.fr/~li

skaller

unread,
Oct 18, 2007, 12:23:11 PM10/18/07
to Brian Hurt, caml...@yquem.inria.fr

On Thu, 2007-10-18 at 10:45 -0400, Brian Hurt wrote:

> I will note that Ocaml's row-level polymorphism allows you to invent new
> supertypes of a given subtype as needed (a real nice feature, IMHO).
> But what he's asking for is fundamentally nonsensical.

No it isn't, its just mis-stated. What he actually wants is projections,
which are perfectly sound. That is, given a concrete record with N
fields, a collection of *views* which only expose a subset of the
fields.

More generally, not just a subset of fields, but a new record
which is the image of a epimorphism (a structure preserving
surjection or 'onto' mapping).

--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

_______________________________________________

Dario Teixeira

unread,
Oct 18, 2007, 12:25:00 PM10/18/07
to caml...@yquem.inria.fr
Hi,

(This is a collective reply to all the issues that were raised by my
previous message; sorry if I don't answer each message individually).

I made the early "reverse inheritance" suggestion somewhat facetiously,
but judging from the replies, there was a certain amount of confusion
about what I meant.

I'll try to describe what I have in mind. I'll do it by modelling
the problem in an imaginary OCaml-derived language that features
"reverse inheritance". Please read on:


The "story" class is one that is fully defined. Think of it as a raw
block of marble from which the non-important pieces can be carved out.
To avoid confusion, let's call a class of this kind a "marble-class":

marble-class story (id, title, intro, body) =
object


method id: int = id
method title: string = title
method intro: string = intro

method body: string = body
end

Now, a "full_story" is one that is also fully defined. Taking as starting
point the "story" block-class previously defined, you don't need to carve
out anything to obtain a full_story:

marble-class full_story (id, title, intro, body) =
object
carves story (id, title, intro, body)
end


However, a "blurb_story" does introduce changes: it can be formed by
taking the original story as a starting point, and removing the "body"
chunk of marble. Note that I am using some new keywords: carves, removes,
and CARVED:

marble-class blurb_story (id, title, intro) =
object
carves story (id, title, intro, CARVED)
removes method body
end


Similarly, a "fresh_story" is a story without the "id" field:

marble-class fresh_story (title, intro, body)
object
carves story (CARVED, title, intro, body)
removes method id
end


Now, if you think in terms of traditional inheritance, a blurb_story
"B" is also a story "A", and so on (with OCaml's subtyping inheritance,
the semantics are a bit different, but let's not go there right now).
Therefore you would expect that any method that works on A should also
work on B. But here it's precisely the opposite: anything that works on
B should also work on A, but not the other way around. If the confusion
is purely linguistic, then let's not call it "reverse inheritance";
let's use "marble carving" instead. (Note that for these semantics
to be correct, then a carved-class is not allowed to add new methods,
but only to remove existing methods from the class it is carving).

Finally the big question: is this useful or even feasible to implement?
Probably not. But for the problem I have at hand, this is still the
modelling that best fits my mental picture of the relation between the
various story types.

Cheers,
Dario

___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

_______________________________________________

Dario Teixeira

unread,
Oct 18, 2007, 12:31:09 PM10/18/07
to skaller, Brian Hurt, caml...@yquem.inria.fr
> No it isn't, its just mis-stated. What he actually wants is projections,
> which are perfectly sound. That is, given a concrete record with N
> fields, a collection of *views* which only expose a subset of the
> fields.
>
> More generally, not just a subset of fields, but a new record
> which is the image of a epimorphism (a structure preserving
> surjection or 'onto' mapping).

Hi,

I wasn't familiar with the "projections" terminology, but that is
precisely what I meant, and what I've referred to as "marble carving"!
(The wheel keeps getting reinvented...)

Incidentally, are projections implemented by any language out there?
(I realise that to a certain extent, SQL views are also projections.
But what about other examples?)

Cheers,
Dario

___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

_______________________________________________

Vincent Aravantinos

unread,
Oct 18, 2007, 12:35:50 PM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr

Le 18 oct. 07 à 18:24, Dario Teixeira a écrit :

> Hi,
>
> (This is a collective reply to all the issues that were raised by my
> previous message; sorry if I don't answer each message individually).

(...)

> Finally the big question: is this useful or even feasible to
> implement?
> Probably not. But for the problem I have at hand, this is still the
> modelling that best fits my mental picture of the relation between the
> various story types.

Doesn't it much look like what Zheng Li has just posted ?

V.

William D. Neumann

unread,
Oct 18, 2007, 12:38:02 PM10/18/07
to Zheng Li, caml...@inria.fr
On Thu, 18 Oct 2007 18:13:05 +0200, Zheng Li wrote

> Strangely though, there seems to be a bug in the OO type system:
> (The solution proposed above is safe, as it does coercion)
>
> # let coredump = object inherit templt end;;
> val coredump : templt = <obj>
> # coredump#title;;
>
> Process caml-toplevel segmentation fault

Hmmm... interesting. You can apparently create immediate objects with
virtual instance variables, so long as you don't have virtual methods.

Objective Caml version 3.10.0

# let bad = object method virtual ooops : int end;;
Characters 10-47:
let bad = object method virtual ooops : int end;;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This class should be virtual. The following methods are undefined : ooops
# let bad_int = object val virtual x : int method get_x = x end;;
val bad_int : < get_x : int > = <obj>
# let bad_str = object val virtual x : string method get_x = x end;;
val bad_str : < get_x : string > = <obj>
# bad_int # get_x;;
- : int = 0
# bad_str # get_x;;

*boom!*

--

William D. Neumann

skaller

unread,
Oct 18, 2007, 12:39:53 PM10/18/07
to Arnaud Spiwack, caml...@yquem.inria.fr

On Thu, 2007-10-18 at 17:02 +0200, Arnaud Spiwack wrote:
> >
> > Saying that A is a supertype of B is the equivelent of saying B is a
> > subtype of A. Same relation, different direction. In OO lingo, how
> > they say "B is a subtype of A" is that "B inherits from (is a subclass
> > of) A".
> That's not true. That's how you say it in *some* Object Oriented
> languages. Inheritance is a tool which allows you to derive another
> object from another one. You like it to be a subtype though, it usually
> is rather difficult to build a sound inheritance system where it can
> break subtyping anyway

That isn't so, almost ALL derived types fail to be subtypes.
In particular, this is always the case when there is a mutator,
because derived values are covariant but mutators are contravariant,
on the other hand Object Orientation intrinsically deals with
mutable state .. QED.

This combination destroys the OO paradigm instantly,
[quite apart from the usual linearity constraint which
make it impossible for OO to handle any kind of relationship]

To understand this simply consider a matrix (base type)
and a symmetric matrix (derived type). Every symmetric
matrix 'is-a' matrix. Fine. It works as a value.

But add a mutator, 'setxy(x,y)' with property that only element x,y
is modified and you're screwed: derived class mutators
work on the base, but not the other way around.

The soundness theory is simple: methods have to be covariant,
but mutators have to be contravariant, and since mutators
are also method they have to be *invariant*, and invariance
effectively means an OO class cannot have subtypes.

The effect is, given some class type T, the ONLY thing
you are allowed to do with inheritance is implement it,
you cannot actually derive a new type.

[Now someone who understands the theory better than me
can re-explain this ..:]


--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

_______________________________________________

Brian Hurt

unread,
Oct 18, 2007, 12:43:38 PM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr
Dario Teixeira wrote:

So now you've defined full_story, blurb_story, and fresh_story as
types. Now, I write:

let get_body (story: full_story) = story#get_body;;

let my_blurb = new blurb_story(id, "A Title", "An Intro");;

let my_body = get_body my_blurb;;

what happens?

Brian

Arnaud Spiwack

unread,
Oct 18, 2007, 12:49:46 PM10/18/07
to caml...@yquem.inria.fr
skaller wrote :

> The soundness theory is simple: methods have to be covariant,
> but mutators have to be contravariant, and since mutators
> are also method they have to be *invariant*, and invariance
> effectively means an OO class cannot have subtypes.
>
You got that part wrong. I means that subtypes have the same types for
the same methods (not subtypes, exactly the same types), but it can be
extended with extra methods (you can be slighlty looser using
specialisation of the self-type). That's how things work.


And inheritance usually needs to enforce the same invariant condition,
for the same reasons (since other method inside the inherited class
could make black-box uses of the overridden method... its type cannot
change in general). Thus that goes to "generally, all inherited values
are subtypes" (rational is : if you need to enforce a certain subtyping
property, it needs to be enforced internally during inheritance as well).


Arnaud Spiwack

William D. Neumann

unread,
Oct 18, 2007, 1:04:54 PM10/18/07
to Brian Hurt, Dario Teixeira, caml...@yquem.inria.fr
On Thu, 18 Oct 2007 12:43:10 -0400, Brian Hurt wrote

> So now you've defined full_story, blurb_story, and fresh_story as
> types. Now, I write:
>
> let get_body (story: full_story) = story#get_body;;
>
> let my_blurb = new blurb_story(id, "A Title", "An Intro");;
>
> let my_body = get_body my_blurb;;
>
> what happens?

One would assume the same thing that happens if you were to do:

class full_story = object inherit blurb_story val body method body = body
end;;


let get_body (story: full_story) = story#get_body;;
let my_blurb = new blurb_story(id, "A Title", "An Intro");;
let my_body = get_body my_blurb;;

That is an error indicating that my_body has type < id : int; title :
string; summary : string > but is used with type < get_body : string ..>, <
id : int; title : string; summary : string > has no method get_body.

No?

--

William D. Neumann

Dario Teixeira

unread,
Oct 18, 2007, 1:06:05 PM10/18/07
to Brian Hurt, caml...@yquem.inria.fr
> So now you've defined full_story, blurb_story, and fresh_story as
> types. Now, I write:
>
> let get_body (story: full_story) = story#get_body;;
> let my_blurb = new blurb_story(id, "A Title", "An Intro");;
> let my_body = get_body my_blurb;;
>
> what happens?

Hi,

Well, that's only a problem if you insist in thinking in terms
of inheritance. Remember that the semantics of marble-carving
are NOT "all that works on full_story should also work on blurb_story",
but the other way around.

Also, imagine you were to add the "get_body" method to the root
"story" class. The type system in Marble-Caml is smart enough
to tell you that since the blurb_story carved out the "body" field,
then it must also carve out methods that use it. Aren't imaginary
languages wonderfull?... :-)

(In addition, note that in any real world situation, it wouldn't
make any sense for the user to want to invoke get_body on a blurb
object -- after all, by definition blurbs have no body).

Cheers,
Dario

___________________________________________________________
Yahoo! Answers - Got a question? Someone out there knows the answer. Try it
now.
http://uk.answers.yahoo.com/

_______________________________________________

Brian Hurt

unread,
Oct 18, 2007, 1:22:29 PM10/18/07
to Dario Teixeira, caml...@yquem.inria.fr
Dario Teixeira wrote:

>>So now you've defined full_story, blurb_story, and fresh_story as
>>types. Now, I write:
>>
>>let get_body (story: full_story) = story#get_body;;
>>let my_blurb = new blurb_story(id, "A Title", "An Intro");;
>>let my_body = get_body my_blurb;;
>>
>>what happens?
>>
>>
>
>Hi,
>
>Well, that's only a problem if you insist in thinking in terms
>of inheritance. Remember that the semantics of marble-carving
>are NOT "all that works on full_story should also work on blurb_story",
>but the other way around.
>
>
>

So all full stories are also blurbs, but blurbs are not full stories.
So full stories are a more specific type than blurbs are, because while
we can always use a full story as a blurb, we can't use a blurb as a
full story.

>Also, imagine you were to add the "get_body" method to the root
>"story" class. The type system in Marble-Caml is smart enough
>to tell you that since the blurb_story carved out the "body" field,
>then it must also carve out methods that use it. Aren't imaginary
>languages wonderfull?... :-)
>
>
>

It's much easier, rather than detecting when things have to be removed,
to simply detect when things can be added. Every "remove members"
relationship can be expressed as an "add members" relationship going the
other direction. Start with the simpler (fewer members) type and then
create the derived type by adding members to make the more complex (more
members) type. Anything you can do with one direction of deriving types
you can do with the other direction of deriving.

Which is why Robert said you had just reinvented inheritance.

>(In addition, note that in any real world situation, it wouldn't
>make any sense for the user to want to invoke get_body on a blurb
>object -- after all, by definition blurbs have no body).
>
>

Yep.

Brian

skaller

unread,
Oct 18, 2007, 1:47:44 PM10/18/07
to Arnaud Spiwack, caml...@yquem.inria.fr

On Thu, 2007-10-18 at 18:49 +0200, Arnaud Spiwack wrote:
> skaller wrote :
> > The soundness theory is simple: methods have to be covariant,
> > but mutators have to be contravariant, and since mutators
> > are also method they have to be *invariant*, and invariance
> > effectively means an OO class cannot have subtypes.
> >
> You got that part wrong. I means that subtypes have the same types for
> the same methods (not subtypes, exactly the same types), but it can be
> extended with extra methods (you can be slighlty looser using
> specialisation of the self-type). That's how things work.

I don't think so but I may have misunderstood:
the problem is, you're thinking of values
again. With a value subtype, the extra invariant adds extra
structure which can admit new interesting theorems, so
you can add new methods to calculate them.

But that never happens with mutators because you cannot
HAVE any additional invariants. The derived type has
to have exactly the same set of values as the base,
in which case you cannot have any new interesting
methods -- any method you have can always be defined
in terms of the characteristic methods already in
the base (assuming the whole public state is accessible
of course).

Note I am simplifying since real objects are a mix
of mutable and immutable state, in that case
of course you can subtype the immutable state and leave
the mutable state invariant. But that's just factorisation
which complicates the argument.

>From that view point there is actually only ONE possible
mutator: assignment (since, you can always construct the
new state in a new object and assign it, there is no need
for any partial mutators). For example for a mutable
complex number you do not need 'set_real(), set_imaginary()'
mutators, you only need a constructor Cart(r,i) plus assignment.


--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net

_______________________________________________

Dario Teixeira

unread,
Oct 18, 2007, 1:58:30 PM10/18/07
to Brian Hurt, caml...@yquem.inria.fr
Hi,

> So all full stories are also blurbs, but blurbs are not full stories.
> So full stories are a more specific type than blurbs are, because while
> we can always use a full story as a blurb, we can't use a blurb as a
> full story.

Nope, nothing like that. Read carefully what I wrote: I simply said
that any method that works on a blurb should also work on a full story.
I didn't say anything about reversing the IS-A relation (and yes, I know
that OCaml's inheritance has different semantics, but I am talking here
about OOP in general).

In fact, a blurb is a kind of story. Just one that has been restricted
such that some of story's fields/methods cannot be used on blurb. Now
the OOP purists will jump and say that this violates inheritance. Which
is precisely my point and why I don't call it inheritance!


> It's much easier, rather than detecting when things have to be removed,
> to simply detect when things can be added. Every "remove members"
> relationship can be expressed as an "add members" relationship going the
> other direction. Start with the simpler (fewer members) type and then
> create the derived type by adding members to make the more complex (more
> members) type. Anything you can do with one direction of deriving types
> you can do with the other direction of deriving.

That is true for most problems. However, I just described one particular
class of problem where it is more natural (and correct if you will) to
derive by deconstruction rather than by construction.

Cheers,
Dario

___________________________________________________________
Want ideas for reducing your carbon footprint? Visit Yahoo! For Good http://uk.promotions.yahoo.com/forgood/environment.html

_______________________________________________

Robert Fischer

unread,
Oct 18, 2007, 3:56:01 PM10/18/07
to skaller, caml...@yquem.inria.fr

> But that never happens with mutators because you cannot
> HAVE any additional invariants. The derived type has
> to have exactly the same set of values as the base,
> in which case you cannot have any new interesting
> methods -- any method you have can always be defined
> in terms of the characteristic methods already in
> the base (assuming the whole public state is accessible
> of course).
>
This is why you 1) define a base class which has a very loose set of
invariants or 2) obscure state from inheriting classes.

~~ Robert.

Jacques Garrigue

unread,
Oct 18, 2007, 8:59:37 PM10/18/07
to wneu...@cs.unm.edu, l...@pps.jussieu.fr, caml...@inria.fr
From: "William D. Neumann" <wneu...@cs.unm.edu>

> On Thu, 18 Oct 2007 18:13:05 +0200, Zheng Li wrote
>
> > Strangely though, there seems to be a bug in the OO type system:
> > (The solution proposed above is safe, as it does coercion)
> >
> > # let coredump = object inherit templt end;;
> > val coredump : templt = <obj>
> > # coredump#title;;
> >
> > Process caml-toplevel segmentation fault
>
> Hmmm... interesting. You can apparently create immediate objects with
> virtual instance variables, so long as you don't have virtual methods.
>
> Objective Caml version 3.10.0
>
> # let bad = object method virtual ooops : int end;;
> Characters 10-47:
> let bad = object method virtual ooops : int end;;
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> This class should be virtual. The following methods are undefined : ooops
> # let bad_int = object val virtual x : int method get_x = x end;;
> val bad_int : < get_x : int > = <obj>
> # let bad_str = object val virtual x : string method get_x = x end;;
> val bad_str : < get_x : string > = <obj>
> # bad_int # get_x;;
> - : int = 0
> # bad_str # get_x;;

Virtual fields are a new and experimental feature in 3.10.
The above bug was fixed shortly after the release.

Jacques Garrigue

Ed Keith

unread,
Oct 19, 2007, 9:09:16 AM10/19/07
to William D. Neumann, Dario Teixeira, Robert Fischer, caml...@yquem.inria.fr

I'm new to Ocaml, but in C++ the solution would be to
have an abstract base class 'story' and have
'full_story' and 'blurb' inherit from it.

-EdK

Ed Keith
e_...@yahoo.com

Blog: edkeith.blogspot.com

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

0 new messages