Version française
Home     About     Download     Resources     Contact us    
Browse thread
Smells like duck-typing
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Dario Teixeira <darioteixeira@y...>
Subject: Smells like duck-typing

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

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.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.title
	| `Blurb s -> Printf.printf "%d: %s\n" 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) =
   	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"

   class full (id, title, intro, body) =
   	inherit story (Some id, Some title, Some intro, Some body)
   class blurb (id, title, intro) =
   	inherit story (Some id, Some title, Some intro, None)
   class fresh (title, intro, body) =
   	inherit story (None, Some title, Some intro, Some body)

   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?


Yahoo! Answers - Got a question? Someone out there knows the answer. Try it