Browse thread
environment idiom
[
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: | Michael Walter <michael.walter@g...> |
| Subject: | Re: [Caml-list] environment idiom |
Is there such thing as implicit parameters in O'caml, basically
variables with dynamic extent. Could be (partially?) statically
checked, too, no?
Cheers,
Michael
On Thu, 09 Dec 2004 13:47:35 +0900 (JST), Jacques Garrigue
<garrigue@math.nagoya-u.ac.jp> wrote:
> From: "HENRIKSON, JEFFREY" <JEFHEN@safeco.com>
>
>
>
> > I am interested in the idiom of passing a number of parameters by some
> > kind of "environment" variable. Think of a web server with hundredes of
> > functions for processing markup and other things, only 3 of which need
> > to detect the browser. It's bad maintainability to explicitly pass
> > browserid through hundreds of functions which don't use it. And of
> > course, we must separate the state of the calling threads so as to not
> > cheat with global variables or some such thing.
> >
> > There seem to be two main candidates for such an idiom in Ocaml, objects
> > and polymorphic variants.
>
> [...]
>
> > And the polymorphic variant way, roughly:
> > let h = Hashtbl.create 10;;
> >
> > Hashtbl.add h `Banana (`Banana "b");;
> > Hashtbl.add h `Apple (`Apple "a");;
> > Hashtbl.add h `Pear (`Pear "p");;
>
> This seems a bit of an overkill: I would rather write directly
> let env = [`Banana "b"; `Apple "a"; `Pear "p"]
> Which would give you more or less the same typing.
>
> > Each of these idioms has its own advantage:
> >
> > In the object way the compiler verifies that the functions are passed
> > objects which contain all their needed configuration keys. But if I
> > understand correctly, we must at some point construct an environment
> > object which has _all_ the keys, even if we don't know them yet. We can
> > add by mutation, but we cannot simply leave them out and add them as we
> > get to functions which need them.
>
> A common way to do it would be to have a class defining defaults,
> and inherit and override it in your environment object.
> I'm not sure of how you intend to use your environment.
> But you're right that it is difficult to make changes incremental,
> since ocaml have no incrementally extendable records.
>
> Note also that while you cannot extend an object, you can extend a
> class, and you can use local modules to define classes locally.
> Yet, you cannot pass classes (or modules) to functions, so this does not
> solve your problem.
>
> > In the pv way the construction can be made incremental. Ie, if we
> > changed the hashtable to a list or immutable queue, we could add keys as
> > we go. But at least as I have it set up, the variants are not placing
> > restrictions on the existence of keys in the environment, other than
> > saying "we can understand at least this many keys," which is of course
> > meaningless. Is there a way to turn the typing around to say "we
> > require at least these keys"?
>
> No: any variant type is a subtype of a variant type containing more
> keys, so you would be able to cheat anyway.
>
> > In general, what are the typing and run-time limitations around each
> > way?
>
> I think you've described them correctly: objects offer exact typing,
> but cannot be extended incrementally, and lists of porlymorphic
> variants enforce typing but do not guarantee what keys are defined.
>
> In terms of efficiency, objects generate more code, but performance should
> be comparable.
>
> Note that ocaml contains a third way to do that, which in some cases is
> more natural. You can use labelled arguments. This means that you must
> pass all the arguments explicitly, but you are no longer restricted by
> their order. Some arguments may be optional.
> Depending on your goal, this may be the safest way to pass parameters.
>
> let bar ~pear s = pear^s
> let foo ~apple ~banana ~pear s = bar ~pear (apple^banana^s)
>
> You may look at code in lablgtk2 for clever ways to handle long lists
> of parameters this way. (But this doesn't fit your web server
> example.)
>
> A last way, which could work very well with your web server example,
> is to use records in place of objects, and update them using
> with. Then you can use clever typing.
>
> module Option : sig
> type (+'a,+'b) t
> type abs = [`abs|`pre]
> type pre = [`pre]
> val none : ('a, abs) t
> val some : 'a -> ('a, 'b) t
> val get : ('a, pre) t -> 'a
> end = struct
> type ('a,'b) t = 'a option
> type abs = [`abs|`pre]
> type pre = [`pre]
> let none = None
> let some x = Some x
> let get = function Some x -> x | None -> assert false
> end
>
> open Option
>
> type ('a,'b,'c) env =
> {apple: (string, 'b) t; banana: (string, 'a) t; pear: (string, 'c) t}
>
> # let empty = {apple=none; banana=none; pear=none}
> val empty : (abs, abs, abs) env
>
> # let e1 = {empty with pear=some "Williams"}
> val e1 : (abs, abs, 'a) env
> # let e2 = {e1 with apple=some "Golden"; banana=some "Plantin"}
> val e2 : ('a, 'b, 'c) env
>
> # let bar env s =
> get env.pear ^ s
> val bar : ('a, 'b, pre) env -> string -> string
> # let foo env s =
> bar env (get env.apple ^ get env.banana ^ s)
> val foo : (pre, pre, pre) env -> string -> string
>
> # foo e2 "!";;
> - : string = "WilliamsGoldenPlantin!"
>
> # foo e1;;
> This expression has type (Option.abs, Option.abs, 'a) env
> but is here used with type (Option.pre, Option.pre, Option.pre) env
> Type Option.abs = [ `abs | `pre ] is not compatible with type
> Option.pre = [ `pre ]
>
> Polymorphic variants are not essential here.
> They just allow one to forget about some fields of the environment
> without physically modifying it. For instance:
>
> # let forget_pear env = (env :> (_,_,abs) env);;
> val forget_pear : ('a, 'b, [< abs ]) env -> ('a, 'b, abs) env
>
> If you don't need this kind of operation, you could choose a simpler
> interface, which would be enough in most cases.
> module Option : sig
> type (+'a,+'b) t
> type abs
> type pre
> val none : ('a, abs) t
> val some : 'a -> ('a, 'b) t
> val get : ('a, pre) t -> 'a
> end
>
> By the way, I had thought of a clever way to obtain nicer types:
> type 'u env =
> {banana: (string, 'a) t; apple: (string, 'b) t; pear: (string, 'c) t}
> constraint 'u = <banana:'a; apple:'b; pear:'c>
> But after fiddling with it I discovered a serious bug in the type
> checker. Please do not use constraints on type variables that do not
> appear in the type itself until this is solved. Sorry for the
> inconvenience.
>
> Jacques Garrigue
>
>
>
> _______________________________________________
> 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
>