Browse thread
Smells like duck-typing
[
Home
]
[ Index:
by date
|
by threads
]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
| Date: | -- (:) |
| From: | Arnaud Spiwack <aspiwack@l...> |
| Subject: | Re: [Caml-list] Smells like duck-typing |
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
>
>