From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail2-relais-roc.national.inria.fr (mail2-relais-roc.national.inria.fr [192.134.164.83]) by sympa.inria.fr (Postfix) with ESMTPS id A30837EEBF for ; Wed, 19 Aug 2015 18:42:52 +0200 (CEST) Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of art.wendling@gmail.com) identity=pra; client-ip=209.85.214.177; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="art.wendling@gmail.com"; x-sender="art.wendling@gmail.com"; x-conformance=sidf_compatible Received-SPF: Pass (mail2-smtp-roc.national.inria.fr: domain of art.wendling@gmail.com designates 209.85.214.177 as permitted sender) identity=mailfrom; client-ip=209.85.214.177; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="art.wendling@gmail.com"; x-sender="art.wendling@gmail.com"; x-conformance=sidf_compatible; x-record-type="v=spf1" Received-SPF: None (mail2-smtp-roc.national.inria.fr: no sender authenticity information available from domain of postmaster@mail-ob0-f177.google.com) identity=helo; client-ip=209.85.214.177; receiver=mail2-smtp-roc.national.inria.fr; envelope-from="art.wendling@gmail.com"; x-sender="postmaster@mail-ob0-f177.google.com"; x-conformance=sidf_compatible X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A0AYAgDysNRVm7HWVdFdg29pBoMfqW2QEIIrhXsCgT0HTAEBAQEBARIBAQEBAQYLCwkhLoQjAQEBAwESEQQZARsQAggDAQMBCwYDAgsNDR0CAiEBAREBBQEKEgYTEggBB4d2AQMKCA2dPY8/gS8+MYtAgWyCeYsLChknAwpXhQABAQEBBgEBAQEBARYBBQ6KQoEDgk+CNwQHgmmBQwWFbAyGaIU0gxCFBIV8gW2BSkaQaoNPghwSI4EXEQaCHRyBVTwzAYJLAQEB X-IPAS-Result: A0AYAgDysNRVm7HWVdFdg29pBoMfqW2QEIIrhXsCgT0HTAEBAQEBARIBAQEBAQYLCwkhLoQjAQEBAwESEQQZARsQAggDAQMBCwYDAgsNDR0CAiEBAREBBQEKEgYTEggBB4d2AQMKCA2dPY8/gS8+MYtAgWyCeYsLChknAwpXhQABAQEBBgEBAQEBARYBBQ6KQoEDgk+CNwQHgmmBQwWFbAyGaIU0gxCFBIV8gW2BSkaQaoNPghwSI4EXEQaCHRyBVTwzAYJLAQEB X-IronPort-AV: E=Sophos;i="5.15,711,1432591200"; d="scan'208";a="174192531" Received: from mail-ob0-f177.google.com ([209.85.214.177]) by mail2-smtp-roc.national.inria.fr with ESMTP/TLS/RC4-SHA; 19 Aug 2015 18:42:51 +0200 Received: by obbfr1 with SMTP id fr1so9130374obb.1 for ; Wed, 19 Aug 2015 09:42:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; bh=2pMsVle+oaka3NnHweAMYt2HYoOP7D9iVM64mI/yzNA=; b=OdyVgw+zGGSB6tDoZfJEusqFaUryuVOH3Lv1qEZkoZcjnWS/R6exxdOPOFfncpdYJE wFQltf37sEWlEB5A0OHHRYrowGRiSZsA+GBcsWnV7FNYpOIBMWDwwR52gPdmczA+cKoB Co/xCsnhkhID5R2P+pqoErK12IbvmaNyEwFb9/h8+qZ7FjbuIPJlNMgbd61AW8xK33Gg cJ+Q9oCzoKJFTvuEeGTwX+GsGtyuEgm1SLts4TnWwPfAm2xiMH4JcvAGXn2aD48ivjVA ecgKSpuKAtmNHhq4nzs6V6i2DoRTy0x8Fm5Wwf/jcfnsVvBXWkXT5j5ALH74KSrhV2G5 bccg== MIME-Version: 1.0 X-Received: by 10.182.210.194 with SMTP id mw2mr11325815obc.59.1440002569487; Wed, 19 Aug 2015 09:42:49 -0700 (PDT) Received: by 10.202.86.65 with HTTP; Wed, 19 Aug 2015 09:42:49 -0700 (PDT) In-Reply-To: References: <55BF75F6.1040006@bioquant.uni-heidelberg.de> <8E1A640CE3374EB492981ADB0A2DA5C6@erratique.ch> <20150804092633.GC5689@frosties> <5C2023F4AF0549F58F3E4C9A6F8ABE84@erratique.ch> <20150814105551.GD31364@frosties> <55CDD0F2.1090200@zoho.com> <20150818111134.GA11154@frosties> Date: Wed, 19 Aug 2015 18:42:49 +0200 Message-ID: From: Arthur Wendling To: Simon Cruanes Cc: Anthony Tavener , Gabriel Scherer , "caml-list@inria.fr" Content-Type: multipart/alternative; boundary=001a11c22f12a14b17051dacb9d2 Subject: Re: [Caml-list] destructive local opens --001a11c22f12a14b17051dacb9d2 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable While we do not have "unlet", we do have the ability to restrict a module definition by subtyping it to a stricter signature, which achieves the inverse effect: module V2_add =3D (V2 : sig val ( + ) : V2.t -> V2.t -> V2.t end) V2.(ox + oy) -- shadow ox, oy? V2_add.(ox + oy) -- only + We probably don't use the restricted signatures more often because it's quite verbose: we have to repeat the types of each binding, and we can't use the syntax (M : ...) everywhere. Those are syntax errors for example: open (V2 : ...) -- but include (V2 : ...) works! (V2 : ...).( ox + oy ) So we have to go through a named module, and it gets horribly verbose. Maybe we can steal the Haskell solution here, where the tendency is to list every imported binding: import V2 (ox, oy, ...) -- note the lack of types This is (sometimes) nicer for a reader, since the origin of a name is directly visible, and it's explicit that we only use a subset of each opened module -- so that you know which definitions of V2 you have to check. Now, let's imagine that we had such a light syntax, maybe: (M with val ox, oy, (+), ...) -- for the vague resemblance with existing syntax (M for ox, oy, ...) -- looks lighter, but the keyword is unfortunate then we could write this explicit form, which should not deserve any warning: let ox =3D (V2 for dot, ( * )).((dot v ox) * ox) in (V2 for ( * ), ( + )).(3 * ox + oy) let open (V2 for dot, ( + ), ( * )) in let ox =3D (dot v ox) * ox in 3 * ox + oy Sure, it's a lot more explicit than the other alternatives proposed in this thread, but it might also be beneficial outside of the M.( ... ) sugar situation. Random clarifications: - (M for x, ...) would be a shortcut for (M : sig val x : (module type of M).x ... end), which isn't even valid. - We would probably want (M for type t) too, but constructors and record fields are problematic, since we have to import them all (?). - (M for x as y) could be a nice extension for renaming and not shadow x. - Bonus: open (M for x) can correctly report M.x as missing if it has been renamed. Also, while I would appreciate "unlet x in", I don't think that we want to be negatively informative when it comes to opening a module: (M without x) isn't failproof regarding what remains; and the proposed annotation [@shadow x] looks good enough for the purpose of scripting the warning heuristic. On Wed, Aug 19, 2015 at 5:55 PM, Simon Cruanes wrote: > This whole thread makes me wonder whether local opens are worth it. I > don't like global open (at all), and shadowing is harmful even in smaller > scopes. Local open seems to be used for DSL that have a lot of infix > operators (maths, etc.) as demonstrated by the proposal of new warnings a= nd > syntax about shadowing of infix operators. > > If modules have short names (Z, Q from Zarith come to mind, but > module-aliasing your favorite monad to "M" would do too), would M.+ be a > reasonable infix operator? I would be ready to have slightly more verbose > calls to M.>>=3D if it removes ambiguity and potential shadowing bugs. Of > course I don't know if this is compatible with the current syntax. > > > Le 19 ao=C3=BBt 2015 00:26:00 UTC+02:00, Anthony Tavener < > anthony.tavener@gmail.com> a =C3=A9crit : >> >> (TL;DR: I commonly want to specify paths -uniquely- for identifiers in >> local >> scopes (function, module) which have no means to do this.) >> >> As I run into this yet again, moments ago... >> >> I do often want the ability to be clear that something is not to be >> shadowed >> by the opened scope... to specify it's from the local (usually function) >> scope. >> Part of the reason is for code clarity, but also to safeguard against >> possible >> later changes in the *opened* module (introducing the same identifier). >> >> let init flags =3D >> M.(init (flag1 + flag2 + flags)) (* flags is intended to be 'local', >> but it could be shadowed by a value in M *) >> >> Where M provides 'init', 'flag1', 'flag2', and '+', but 'flags' is in the >> local (function) context. When I do this I try to think of a way to make >> it >> self evident that 'flags' is not from M, but there is no way. Aside from >> bringing it outside the local-open, but then it's more difficult to build >> an expression. >> >> Vimscript might be one of the worst languages to use as a reference, but >> this >> issue does bring to mind the scope prefixes... >> >> let init flags =3D >> M.(init (flag1 + flag2 + l:flags)) (* illustrative -- not a proposal! >> *) >> >> I sometimes consider using naming conventions, but I don't want to >> explicitly >> make function arguments something like l_flags, l_point, etc. That would >> be a >> horrible widespread style, and doesn't work nicely with named arguments. >> Plus, changing names to do this seems wrong -- it's at the access site >> where >> you want to disambiguate, which always leads me to think some sigil or >> prefix. >> >> There was an earlier sidetrack which went with ^ as an "unopen" prefix. >> At first, >> my interest was piqued. Naturally, the issue of recursive unopen came >> up... >> >> In response, Gabriel wisely remarked: >> >> "It is remarkable that programming languages have avoided introducing >> explicit weakening (the popping of a symbol out of scope) for now, and >> I think it is a property that should be preserved. We're not yet ready >> to go there." >> >> Good advice when the thread was spinning out of control and probably not >> going >> to settle on anything realistic or favorable. Even though there might be >> merit >> in pursuing fine-grained scope-popping as its own topic. >> >> I think there is a simpler gain to be had from the idea of being able to >> specify >> the path of the current context. "Current context" would need to be >> something sensible, and I'm not sure yet what would be best, as there is= a >> related issue I encounter commonly: >> >> A way to specify the path of the current module. >> >> There is no way to do this, right? If I'm in "a.ml", I can't refer to >> A.identifier, and there is no other way to uniquely specify the path to >> what >> *will become* A.identifier? As the bare "identifier" can be shadowed by >> any >> modules opened afterward. Unlike the general "scope-popping", there is >> also >> a common language feature like this: self or this. >> >> I usually want to be explicit with module paths, especially if I am using >> an >> "identifier" which could reasonably be expected to exist now or later in >> the >> other modules being used. I do keep opens to a minimum, but often an >> entire >> expression will be in a local open (to bring in operators), and there, >> again... I would like that clarity, and safeguard against changes which >> might >> happen in the other modules, leading to suprises or need to change *this* >> module for no good reason other than a naming conflict which ideally can >> be >> prepared against. >> >> Has there been any discussion about referring to the local module? My >> guess is >> that it might be a mild enough problem to not warrant any proposed >> solutions. >> But if there are ideas, maybe the same thing or something similar can al= so >> apply to this problem of "escaping" a local open? They are very similar, >> but >> one is module-scope, while I think the other would be function-scope >> (though >> module-scope might imply the "right thing" anyway)... I'm not certain, as >> haven't been keeping track of the cases I encounter, and others might ha= ve >> different use-cases. >> >> >> On Tue, Aug 18, 2015 at 7:00 AM, Gabriel Scherer < >> gabriel.scherer@gmail.com> wrote: >> >>> Note that the dual feature does not exist for variant constructors, >>> because it is easy to define only on the constructor at the toplevel of= the >>> pattern, and nested patterns get us in muddy waters. >>> >>> On Tue, Aug 18, 2015 at 2:52 PM, David Allsopp >>> wrote: >>> >>>> Goswin von Brederlow wrote: >>>> > On Fri, Aug 14, 2015 at 01:28:50PM +0200, Drup wrote: >>>> > > >>>> > > >You can't qualifylocal values or values of the surrounding module >>>> so >>>> > > >that is a no go. >>>> > > > >>>> > > >I also often use local open to access records, as in: >>>> > > > >>>> > > >let r =3D M.({ x =3D 1; y; z =3D depth; }) in >>>> > > >>>> > > You can avoid the local open altogether and write it like that: >>>> > > >>>> > > let r =3D {M. x =3D 1; y; z =3D depth } in >>>> > > >>>> > > It's even shorter. >>>> > >>>> > That only works because newer ocaml disambiguises (is that a word?) >>>> record >>>> > So it's implicitly using M.y =3D y and M.z =3D depth. >>>> > labels when it determines the record type from the first label, righ= t? >>>> >>>> Only since you ask: "disambiguates" :o) That said, it's quite common to >>>> see words like "disambiguises" being invented by Americans! >>>> >>>> But this isn't related to the disambiguation features of OCaml 4.01+. >>>> Those allow you to write things like: >>>> >>>> type t =3D {x : int} >>>> type u =3D {x : int; y : string} >>>> >>>> let foo =3D {x =3D 1} >>>> let bar =3D {x =3D 42; y =3D ""} >>>> >>>> This is actually a much older notation added in OCaml 3.08. Prior to >>>> that, if you hadn't opened a module you had to qualify each label: >>>> >>>> {M.x =3D 1; M.y =3D y; M.z =3D depth} >>>> >>>> but this was "silly", since it's not possible to use non-equivalent >>>> module paths for labels, so OCaml 3.08 changed it so that you only nee= ded >>>> to put the module path on one label (and it doesn't have to be the fir= st >>>> one, it's just a bit weird to put it in the middle!). >>>> >>>> OCaml 3.12 added, amongst other record-related goodies, the shorthand >>>> {y} to mean {y =3D y}. So while you can use local open as you're using= it, >>>> you've been able to do it as a totally unambiguous language feature for >>>> quite some time. >>>> >>>> >>>> David >>>> >>>> >>>> -- >>>> Caml-list mailing list. Subscription management and archives: >>>> https://sympa.inria.fr/sympa/arc/caml-list >>>> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners >>>> Bug reports: http://caml.inria.fr/bin/caml-bugs >>>> >>> >>> >> > -- > Simon > --001a11c22f12a14b17051dacb9d2 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
While we do not have "unlet", we do have the abi= lity to restrict a module definition by subtyping it to a stricter signatur= e, which achieves the inverse effect:

=C2=A0=C2=A0=C2=A0 module V2_a= dd =3D (V2 : sig val ( + ) : V2.t -> V2.t -> V2.t end)
=C2=A0=C2= =A0=C2=A0 V2.(ox + oy)=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 -- shadow ox, oy?
= =C2=A0=C2=A0=C2=A0 V2_add.(ox + oy)=C2=A0 -- only +

We probably don&= #39;t use the restricted signatures more often because it's quite verbo= se: we have to repeat the types of each binding, and we can't use the s= yntax (M : ...) everywhere. Those are syntax errors for example:

=C2= =A0=C2=A0=C2=A0 open (V2 : ...)=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 -- but include (V2 : ...) works!
=C2=A0=C2=A0=C2=A0 (= V2 : ...).( ox + oy )

So we have to go through a named module, and i= t gets horribly verbose. Maybe we can steal the Haskell solution here, wher= e the tendency is to list every imported binding:

=C2=A0=C2=A0=C2=A0= import V2 (ox, oy, ...)=C2=A0=C2=A0 -- note the lack of types

This = is (sometimes) nicer for a reader, since the origin of a name is directly v= isible, and it's explicit that we only use a subset of each opened modu= le -- so that you know which definitions of V2 you have to check.

No= w, let's imagine that we had such a light syntax, maybe:

=C2=A0= =C2=A0=C2=A0 (M with val ox, oy, (+), ...) -- for the vague resemblance wit= h existing syntax
=C2=A0=C2=A0=C2=A0 (M for ox, oy, ...)=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 -- looks lighter, but the key= word is unfortunate

then we could write this explicit form, which sh= ould not deserve any warning:

=C2=A0=C2=A0=C2=A0 let ox =3D (V2 for = dot, ( * )).((dot v ox) * ox) in
=C2=A0=C2=A0=C2=A0 (V2 for ( * ), ( + )= ).(3 * ox + oy)

=C2=A0=C2=A0=C2=A0 let open (V2 for dot, ( + ), ( * = )) in
=C2=A0=C2=A0=C2=A0 let ox =3D (dot v ox) * ox in
=C2=A0=C2=A0= =C2=A0 3 * ox + oy

Sure, it's a lot more explicit than the other= alternatives proposed in this thread, but it might also be beneficial outs= ide of the M.( ... ) sugar situation.
Random clarifications:
- (M for= x, ...) would be a shortcut for (M : sig val x : (module type of M).x ... = end), which isn't even valid.
- We would probably want (M for type t= ) too, but constructors and record fields are problematic, since we have to= import them all (?).
- (M for x as y) could be a nice extension for ren= aming and not shadow x.
- Bonus: open (M for x) can correctly report M.x= as missing if it has been renamed.

Also, while I would appreciate &= quot;unlet x in", I don't think that we want to be negatively info= rmative when it comes to opening a module: (M without x) isn't failproo= f regarding what remains; and the proposed annotation [@shadow x] looks goo= d enough for the purpose of scripting the warning heuristic.

On Wed, Aug 19, 2015 a= t 5:55 PM, Simon Cruanes <simon.cruanes.2007@m4x.org> wrote:
This whole thread makes me= wonder whether local opens are worth it. I don't like global open (at = all), and shadowing is harmful even in smaller scopes. Local open seems to = be used for DSL that have a lot of infix operators (maths, etc.) as demonst= rated by the proposal of new warnings and syntax about shadowing of infix o= perators.

If modules have short names (Z, Q from Zarith come to mind, but module-alia= sing your favorite monad to "M" would do too), would M.+ be a rea= sonable infix operator? I would be ready to have slightly more verbose call= s to M.>>=3D if it removes ambiguity and potential shadowing bugs. Of= course I don't know if this is compatible with the current syntax.


Le 19 ao=C3=BBt 2015 = 00:26:00 UTC+02:00, Anthony Tavener <anthony.tavener@gmail.com> a =C3=A9crit = :
(TL;DR: I commonly want to specify paths -uniquely- f= or identifiers in local
scopes (function, module) which have no m= eans to do this.)

As I run into this yet again, mo= ments ago...

I do often want the ability to be cle= ar that something is not to be shadowed
by the opened scope... to= specify it's from the local (usually function) scope.
Part o= f the reason is for code clarity, but also to safeguard against possible
later changes in the *opened* module (introducing the same identifi= er).

=C2=A0 let init flags =3D
=C2=A0 = =C2=A0 M.(init (flag1 + flag2 + flags)) (* flags is intended to be 'loc= al', but it could be shadowed by a value in M *)

Where M provides 'init', 'flag1', 'flag2', and &= #39;+', but 'flags' is in the
local (function) contex= t. When I do this I try to think of a way to make it
self evident that 'flags' is not from= M, but there is no way. Aside from
bringing it outside the local= -open, but then it's more difficult to build
an expression.

Vimscript might be one of the worst languages to us= e as a reference, but this
issue does bring to mind the scope pre= fixes...

=C2=A0 let init flags =3D
=C2= =A0 =C2=A0 M.(init (flag1 + flag2 + l:flags)) (* illustrative -- not a prop= osal! *)

I sometimes consider using naming convent= ions, but I don't want to explicitly
make function arguments = something like l_flags, l_point, etc. That would be a
horrible wi= despread style, and doesn't work nicely with named arguments.
Plus, changing names to do this seems wrong -- it's at the access site= where
you want to disambiguate, which always leads me to think s= ome sigil or prefix.

There was an earlier sidetrack which went = with ^ as an "unopen" prefix. At first,
my interest was= piqued. Naturally, the issue of recursive unopen came up...

=
In response, Gabriel wisely remarked:

= =C2=A0"It is remarkable that programming languages have avoided introd= ucing
=C2=A0 explicit weakening (the popping of a symbol out of s= cope) for now, and
=C2=A0 I think it is a property that should be= preserved. We're not yet ready
=C2=A0 to go there."

Good advice when the thread was spinning out of contr= ol and probably not going
to settle on anything realistic or favo= rable. Even though there might be merit
in pursuing fine-grained = scope-popping as its own topic.

I think there is a= simpler gain to be had from the idea of being able to specify
th= e path of the current context. "Current context" would need to be
something s= ensible, and I'm not sure yet what would be best, as there is a
related issue I encounter commonly:

A way to sp= ecify the path of the current module.

There is no = way to do this, right? If I'm in "a.ml", I can't refer to
A.identifier, an= d there is no other way to uniquely specify the path to what
*wil= l become* A.identifier? As the bare "identifier" can be shadowed = by any
modules opened afterward. Unlike the general "scope-p= opping", there is also
a common language feature like this: = self or this.

I usually want to be explicit with m= odule paths, especially if I am using an
"identifier" w= hich could reasonably be expected to exist now or later in the
ot= her modules being used. I do keep opens to a minimum, but often an entire
expression will be in a l= ocal open (to bring in operators), and there,
again... I would li= ke that clarity, and safeguard against changes which might
happen= in the other modules, leading to suprises or need to change *this*
module for no good reason other than a naming conflict which ideally can= be
prepared against.

Has there been any= discussion about referring to the local module? My guess is
that= it might be a mild enough problem to not warrant any proposed solutions.
But if there are ideas, maybe the same thing or something similar = can also
apply to this problem of "escaping" a local op= en? They are very similar, but
one is module-scope, while I think= the other would be function-scope (though
module-scope might imp= ly the "right thing" anyway)... I'm not certain, as
haven't been keeping track of the cases I encounter, and others might have
dif= ferent use-cases.


=
On Tue, Aug 18, 2015 at 7:00 AM, Gabriel Scherer= <gabriel.scherer@gmail.com> wrote:
Note that the dual feature does not exi= st for variant constructors, because it is easy to define only on the const= ructor at the toplevel of the pattern, and nested patterns get us in muddy = waters.

On Tue, Aug 18, 2015 at 2:52 PM, David Allsopp = <dra-news@me= tastack.com> wrote:
G= oswin von Brederlow wrote:
> On Fri, Aug 14, 2015 at 01:28:50PM +0200, Drup wrote:
> >
> > >You can't qualifylocal values or values of the surroundin= g module so
> > >that is a no go.
> > >
> > >I also often use local open to access records, as in:
> > >
> > >let r =3D M.({ x =3D 1; y; z =3D depth; }) in
> >
> > You can avoid the local open altogether and write it like that: > >
> >=C2=A0 =C2=A0 =C2=A0let r =3D {M. x =3D 1; y; z =3D depth } in
> >
> > It's even shorter.
>
> That only works because newer ocaml disambiguises (is that a word?) re= cord
> So it's implicitly using M.y =3D y and M.z =3D depth.=
> labels when it determines the record type from the first label, right?=

Only since you ask: "disambiguates" :o) That said, it'= s quite common to see words like "disambiguises" being invented b= y Americans!

But this isn't related to the disambiguation features of OCaml 4.01+. T= hose allow you to write things like:

type t =3D {x : int}
type u =3D {x : int; y : string}

let foo =3D {x =3D 1}
let bar =3D {x =3D 42; y =3D ""}

This is actually a much older notation added in OCaml 3.08. Prior to that, = if you hadn't opened a module you had to qualify each label:

{M.x =3D 1; M.y =3D y; M.z =3D depth}

but this was "silly", since it's not possible to use non-equi= valent module paths for labels, so OCaml 3.08 changed it so that you only n= eeded to put the module path on one label (and it doesn't have to be th= e first one, it's just a bit weird to put it in the middle!).

OCaml 3.12 added, amongst other record-related goodies, the shorthand {y} t= o mean {y =3D y}. So while you can use local open as you're using it, y= ou've been able to do it as a totally unambiguous language feature for = quite some time.


David


--
Caml-list mailing list.=C2=A0 Subscription management and archives:
https://sympa.inria.fr/sympa/arc/caml-list
Beginner's list: http://groups.yahoo.com/group/ocam= l_beginners
Bug reports: http://caml.inria.fr/bin/caml-bugs



--
Simon

--001a11c22f12a14b17051dacb9d2--