From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by yquem.inria.fr (Postfix) with ESMTP id 4211FBB81 for ; Thu, 9 Dec 2004 07:02:18 +0100 (CET) Received: from pauillac.inria.fr (pauillac.inria.fr [128.93.11.35]) by concorde.inria.fr (8.13.0/8.13.0) with ESMTP id iB962HqW000522 for ; Thu, 9 Dec 2004 07:02:17 +0100 Received: from nez-perce.inria.fr (nez-perce.inria.fr [192.93.2.78]) by pauillac.inria.fr (8.7.6/8.7.3) with ESMTP id HAA15532 for ; Thu, 9 Dec 2004 07:02:17 +0100 (MET) Received: from rproxy.gmail.com (rproxy.gmail.com [64.233.170.198]) by nez-perce.inria.fr (8.13.0/8.13.0) with ESMTP id iB962GdF017598 for ; Thu, 9 Dec 2004 07:02:16 +0100 Received: by rproxy.gmail.com with SMTP id c16so752526rne for ; Wed, 08 Dec 2004 22:02:16 -0800 (PST) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:reply-to:to:subject:cc:in-reply-to:mime-version:content-type:content-transfer-encoding:references; b=Li50RBjfu8Ju1QmCqOEL4rSU93AzFg2whZip7eri9hw5Ga7vZRyksoxNZRicneTffkMTf5tKrN1nOPmdqrqq6yS8h9Z9GGFHkFaQ4R3uUgAYglY8MlWDAo1DSqhjwBN+yBuXbxgL9RoSqPmlyn1hY55lPP+66D2HeH1n4nFbpY8= Received: by 10.38.90.75 with SMTP id n75mr625000rnb; Wed, 08 Dec 2004 22:02:16 -0800 (PST) Received: by 10.38.86.10 with HTTP; Wed, 8 Dec 2004 22:02:15 -0800 (PST) Message-ID: <877e9a170412082202790e2cfb@mail.gmail.com> Date: Thu, 9 Dec 2004 01:02:16 -0500 From: Michael Walter Reply-To: Michael Walter To: Jacques Garrigue Subject: Re: [Caml-list] environment idiom Cc: JEFHEN@safeco.com, caml-list@inria.fr In-Reply-To: <20041209.134735.79249569.garrigue@math.nagoya-u.ac.jp> Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit References: <9410EC84C0872141B27A2726613EF45D02A52E08@psmrdcex01.psm.pin.safeco.com> <20041209.134735.79249569.garrigue@math.nagoya-u.ac.jp> X-Miltered: at concorde with ID 41B7EA69.002 by Joe's j-chkmail (http://j-chkmail.ensmp.fr)! X-Miltered: at nez-perce with ID 41B7EA68.000 by Joe's j-chkmail (http://j-chkmail.ensmp.fr)! X-Spam: no; 0.00; caml-list:01 o'caml:01 statically:01 cheers:01 wrote:01 henrikson:01 threads:01 ocaml:01 variants:01 hashtbl:01 hashtbl:01 overkill:01 idioms:01 compiler:01 mutation:01 X-Spam-Checker-Version: SpamAssassin 3.0.0 (2004-09-13) on yquem.inria.fr X-Spam-Status: No, score=0.0 required=5.0 tests=RCVD_BY_IP autolearn=disabled version=3.0.0 X-Spam-Level: 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 wrote: > From: "HENRIKSON, JEFFREY" > > > > > 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 = > 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 >