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: | 2004-12-09 (06:02) |
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 >