caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Arnaud Spiwack <aspiwack@lix.polytechnique.fr>
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] Smells like duck-typing
Date: Wed, 17 Oct 2007 16:13:10 +0200	[thread overview]
Message-ID: <47161876.7050500@lix.polytechnique.fr> (raw)
In-Reply-To: <932096.75090.qm@web54602.mail.re2.yahoo.com>

Hi,

I think your solution is to fully use the "Objective" part of OCaml, 
that is, using subtyping (technically it's polymorphic rows typing) :

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

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


# let print_metadata s = Printf.printf "%d: %s\n" s#id s#title
val print_metadata : < id : int; title : string; .. > -> unit = <fun>

# print_metadata (new full_t (1, "title", "intro", "body"));;
1: title

# print_metadata (new blurb_t (1, "title", "intro"));;
1: title

Dario Teixeira a écrit :
> 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
>
>   


  reply	other threads:[~2007-10-17 14:13 UTC|newest]

Thread overview: 55+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-17 13:35 Dario Teixeira
2007-10-17 14:13 ` Arnaud Spiwack [this message]
2007-10-17 14:47   ` [Caml-list] " Dario Teixeira
2007-10-17 14:25 ` Daniel Bünzli
2007-10-17 15:03   ` skaller
2007-10-17 15:13     ` Dario Teixeira
2007-10-17 15:25       ` Arnaud Spiwack
2007-10-17 15:32       ` Daniel Bünzli
2007-10-17 16:21         ` Chris King
2007-10-18  7:28           ` Stefano Zacchiroli
2007-10-18  8:33             ` [ANN] pa_oo and pa_polymap for 3.10 (Re: [Caml-list] Smells like duck-typing) Jacques Garrigue
2007-10-17 16:57         ` [Caml-list] Smells like duck-typing skaller
2007-10-17 16:52       ` skaller
2007-10-17 16:59         ` Robert Fischer
2007-10-17 14:33 ` Chris King
2007-10-17 14:59   ` Dario Teixeira
2007-10-17 15:24 ` Vincent Aravantinos
2007-10-17 15:26 ` Zheng Li
2007-10-18 16:13   ` Zheng Li
2007-10-18 16:37     ` [Caml-list] " William D. Neumann
2007-10-19  0:58       ` Jacques Garrigue
2007-10-17 19:59 ` [Caml-list] " Richard Jones
2007-10-17 20:24 ` Dario Teixeira
2007-10-18  7:37   ` Stefano Zacchiroli
2007-10-18 10:31     ` Dario Teixeira
2007-10-18 10:37       ` Stefano Zacchiroli
2007-10-18 13:28       ` Robert Fischer
2007-10-18 14:10         ` Dario Teixeira
2007-10-18 14:18           ` Brian Hurt
2007-10-18 14:29             ` Arnaud Spiwack
2007-10-18 14:45               ` Brian Hurt
2007-10-18 15:02                 ` Arnaud Spiwack
2007-10-18 15:07                   ` Robert Fischer
2007-10-18 15:14                     ` Arnaud Spiwack
2007-10-18 16:39                   ` skaller
2007-10-18 16:49                     ` Arnaud Spiwack
2007-10-18 17:47                       ` skaller
2007-10-18 19:55                         ` Robert Fischer
2007-10-18 16:22                 ` skaller
2007-10-18 16:30                   ` Dario Teixeira
2007-10-18 14:58           ` Robert Fischer
2007-10-18 15:11             ` William D. Neumann
2007-10-18 15:47               ` Loup Vaillant
2007-10-18 16:08                 ` William D. Neumann
2007-10-19 13:08               ` Ed Keith
2007-10-18 16:24           ` Dario Teixeira
2007-10-18 16:35             ` Vincent Aravantinos
2007-10-18 16:43             ` Brian Hurt
2007-10-18 17:04               ` William D. Neumann
2007-10-18 17:05               ` Dario Teixeira
2007-10-18 17:22                 ` Brian Hurt
2007-10-18 17:58                   ` Dario Teixeira
2007-10-18 15:42 ` Vincent Aravantinos
     [not found] <47161E3B.3060704@tsc.uc3m.es>
2007-10-17 15:01 ` Dario Teixeira
2007-10-17 20:20   ` Alain Frisch

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=47161876.7050500@lix.polytechnique.fr \
    --to=aspiwack@lix.polytechnique.fr \
    --cc=caml-list@yquem.inria.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).