* [Caml-list] destructive local opens @ 2015-08-03 13:39 Nils Becker 2015-08-03 13:43 ` Thomas Refis 2015-08-03 13:45 ` Daniel Bünzli 0 siblings, 2 replies; 59+ messages in thread From: Nils Becker @ 2015-08-03 13:39 UTC (permalink / raw) To: caml-list Hi, this is a small syntax suggestion which I thought I should share; apologies if this is nonsense or has been already decided against. Currently we have let open! M in ... to indicate that we do intend to shadow definitions in the current scope by those in M. This avoids getting warning 44 on compilation. I find that quite useful. Would it be a good idea to have the same thing for local opens with the dot syntax? M.(...) produces warning 44 currently, but a proposed new syntax M.!(...) or M!(...) or something of the kind would suppress it. Obviously it should not interfere with references. I came across this wish after defining a small Vector module which overrides (+) to mean vector addition. Then formulas are conveniently written M.(v + w) but that means I get 44. Of course I could define (+|) instead but why not make use of namespaces if we can. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 13:39 [Caml-list] destructive local opens Nils Becker @ 2015-08-03 13:43 ` Thomas Refis 2015-08-03 13:45 ` Daniel Bünzli 1 sibling, 0 replies; 59+ messages in thread From: Thomas Refis @ 2015-08-03 13:43 UTC (permalink / raw) To: Nils Becker; +Cc: caml users https://github.com/ocaml/ocaml/pull/218 2015-08-03 14:39 GMT+01:00 Nils Becker <nils.becker@bioquant.uni-heidelberg.de>: > Hi, > > this is a small syntax suggestion which I thought I should share; > apologies if this is nonsense or has been already decided against. > > Currently we have > > let open! M in > ... > > to indicate that we do intend to shadow definitions in the current scope > by those in M. This avoids getting warning 44 on compilation. I find > that quite useful. > > Would it be a good idea to have the same thing > for local opens with the dot syntax? M.(...) produces warning 44 > currently, but a proposed new syntax > > M.!(...) > or > M!(...) > > or something of the kind would suppress it. Obviously it should not > interfere with references. > > I came across this wish after defining a small Vector module which > overrides (+) to mean vector addition. Then formulas are conveniently > written M.(v + w) but that means I get 44. Of course I could define (+|) > instead but why not make use of namespaces if we can. > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 13:39 [Caml-list] destructive local opens Nils Becker 2015-08-03 13:43 ` Thomas Refis @ 2015-08-03 13:45 ` Daniel Bünzli 2015-08-03 13:47 ` Daniel Bünzli 1 sibling, 1 reply; 59+ messages in thread From: Daniel Bünzli @ 2015-08-03 13:45 UTC (permalink / raw) To: Nils Becker; +Cc: caml-list Le lundi, 3 août 2015 à 14:39, Nils Becker a écrit : > I came across this wish after defining a small Vector module which > overrides (+) to mean vector addition. Then formulas are conveniently > written M.(v + w) but that means I get 44. Of course I could define (+|) > instead but why not make use of namespaces if we can. I think that the whole way 44 is handled is broken. First it should be enabled by default, then it should not warn about infix operator shadowing. I think this would by design lead to good M.() usages and would avoid the need for a new notation (note that it was proposed recently here [1]) Best, Daniel [1] https://github.com/ocaml/ocaml/pull/218 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 13:45 ` Daniel Bünzli @ 2015-08-03 13:47 ` Daniel Bünzli [not found] ` <55BF75F6.1040006@bioquant.uni-heidelberg.de> 0 siblings, 1 reply; 59+ messages in thread From: Daniel Bünzli @ 2015-08-03 13:47 UTC (permalink / raw) To: Nils Becker; +Cc: caml-list Le lundi, 3 août 2015 à 14:45, Daniel Bünzli a écrit : > I think that the whole way 44 is handled is broken. That is on the M.() notation. Daniel ^ permalink raw reply [flat|nested] 59+ messages in thread
[parent not found: <55BF75F6.1040006@bioquant.uni-heidelberg.de>]
* Re: [Caml-list] destructive local opens [not found] ` <55BF75F6.1040006@bioquant.uni-heidelberg.de> @ 2015-08-03 14:24 ` Daniel Bünzli 2015-08-03 14:37 ` Gabriel Scherer ` (2 more replies) 0 siblings, 3 replies; 59+ messages in thread From: Daniel Bünzli @ 2015-08-03 14:24 UTC (permalink / raw) To: Nils Becker; +Cc: Caml-list Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > It's possible that people actually want M.() to mean let open! more > often than let open. For me I think that's the case. If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): let ox = V2.((dot v ox) * ox) in V2.(3 * ox + oy) The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. Best, Daniel [1] http://erratique.ch/software/gg ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 14:24 ` Daniel Bünzli @ 2015-08-03 14:37 ` Gabriel Scherer 2015-08-03 14:43 ` Daniel Bünzli 2015-08-03 15:10 ` octachron 2015-08-04 6:51 ` Petter Urkedal 2015-08-04 9:26 ` Goswin von Brederlow 2 siblings, 2 replies; 59+ messages in thread From: Gabriel Scherer @ 2015-08-03 14:37 UTC (permalink / raw) To: Daniel Bünzli; +Cc: Nils Becker, Caml-list We could split 44 in two warnings, one (52 ?) for alphanumeric identifiers and the other (53 ?) for symbol identifiers (infix and prefix operations, but also now the indexed read and write syntax), and enable 52 by default. The more paranoid people may then enable 53 explicitly (and those that enable 44 explicitly today would retain the current behavior). On Mon, Aug 3, 2015 at 4:24 PM, Daniel Bünzli <daniel.buenzli@erratique.ch> wrote: > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : >> It's possible that people actually want M.() to mean let open! more >> often than let open. For me I think that's the case. > > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): > > let ox = V2.((dot v ox) * ox) in > V2.(3 * ox + oy) > > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. > > Best, > > Daniel > > [1] http://erratique.ch/software/gg > > > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 14:37 ` Gabriel Scherer @ 2015-08-03 14:43 ` Daniel Bünzli 2015-08-03 15:10 ` octachron 1 sibling, 0 replies; 59+ messages in thread From: Daniel Bünzli @ 2015-08-03 14:43 UTC (permalink / raw) To: Gabriel Scherer; +Cc: Nils Becker, Caml-list Le lundi, 3 août 2015 à 15:37, Gabriel Scherer a écrit : > We could split 44 in two warnings, one (52 ?) for alphanumeric > identifiers and the other (53 ?) for symbol identifiers (infix and > prefix operations, but also now the indexed read and write syntax), > and enable 52 by default. Yes please. FWIW here's the original feature request: http://caml.inria.fr/mantis/view.php?id=5980 which has discussions about this. Best, Daniel ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 14:37 ` Gabriel Scherer 2015-08-03 14:43 ` Daniel Bünzli @ 2015-08-03 15:10 ` octachron 2015-08-03 15:22 ` Daniel Bünzli 1 sibling, 1 reply; 59+ messages in thread From: octachron @ 2015-08-03 15:10 UTC (permalink / raw) To: Gabriel Scherer; +Cc: Nils Becker, Caml-list Splitting 44 between alphanumeric and other identifiers is a nice default but it sounds a little arbitrary. Maybe it would make sense to combine this splitting with your [@shadow] annotation idea (cf. https://github.com/ocaml/ocaml/pull/218#issuecomment-123998749) ? Like this, library writers could annotate identifiers that are intended to shadow predefined identifiers without any limitations, and cautious users could activate 53 to protect themselves from unexpected identifiers shadowing? Regards, octachron. On 08/03/15 16:37, Gabriel Scherer wrote : > We could split 44 in two warnings, one (52 ?) for alphanumeric > identifiers and the other (53 ?) for symbol identifiers (infix and > prefix operations, but also now the indexed read and write syntax), > and enable 52 by default. The more paranoid people may then enable 53 > explicitly (and those that enable 44 explicitly today would retain the > current behavior). > > > On Mon, Aug 3, 2015 at 4:24 PM, Daniel Bünzli > <daniel.buenzli@erratique.ch> wrote: >> Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : >>> It's possible that people actually want M.() to mean let open! more >>> often than let open. For me I think that's the case. >> If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): >> >> let ox = V2.((dot v ox) * ox) in >> V2.(3 * ox + oy) >> >> The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. >> >> Best, >> >> Daniel >> >> [1] http://erratique.ch/software/gg >> >> >> >> -- >> 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 15:10 ` octachron @ 2015-08-03 15:22 ` Daniel Bünzli 2015-08-03 16:13 ` octachron 0 siblings, 1 reply; 59+ messages in thread From: Daniel Bünzli @ 2015-08-03 15:22 UTC (permalink / raw) To: octachron; +Cc: Gabriel Scherer, Nils Becker, Caml-list Le lundi, 3 août 2015 à 16:10, octachron a écrit : > Splitting 44 between alphanumeric and other identifiers is a nice > default but it sounds a little arbitrary. I don't think it's arbitrary, it matches the use case for the M.() notation. > Like this, library writers could annotate identifiers that are intended > to shadow predefined identifiers without any limitations, and cautious > users could activate 53 to protect themselves from unexpected > identifiers shadowing? Somehow I prefer to have a well defined broad rule, rather than letting library authors micro manage that. The benefit is that I can understand what is happening by only looking at the expression I'm reading. With your proposal I also need to go read the library source to understand what is happening. Best, Daniel ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 15:22 ` Daniel Bünzli @ 2015-08-03 16:13 ` octachron 2015-08-03 16:51 ` Daniel Bünzli 0 siblings, 1 reply; 59+ messages in thread From: octachron @ 2015-08-03 16:13 UTC (permalink / raw) To: Daniel Bünzli; +Cc: Gabriel Scherer, Nils Becker, Caml-list 08/03/15 17:22, Daniel Bünzli wrote : > Le lundi, 3 août 2015 à 16:10, octachron a écrit : >> Splitting 44 between alphanumeric and other identifiers is a nice >> default but it sounds a little arbitrary. > I don't think it's arbitrary, it matches the use case for the M.() notation. > I agree that it matches many use cases of the M.(); but not all of them. And, I am not sure that the splitting should be done on the operators/alphanumeric identifiers criterion: If I define an exotic operator, for instance "@->", it would be very sensible to emit a warning if this exotic operator is shadowed. Similarly, in a Triple module defining a "fst" function is very natural. It could be nice to be able to disactivate the warning on the shadowing of Pervasive 's "fst". For these reasons, I think that it is could be useful to let the choice to library authors. >> Like this, library writers could annotate identifiers that are intended >> to shadow predefined identifiers without any limitations, and cautious >> users could activate 53 to protect themselves from unexpected >> identifiers shadowing? > Somehow I prefer to have a well defined broad rule, rather than letting library authors micro manage that. A broad rule is clearly easier to remember, so it is a question of compromise. > The benefit is that I can understand what is happening by only looking at the expression I'm reading. With your proposal I also need to go read the library source to understand what is happening. > I think that it is partially true. For instance, with a vector library, "*" is always quite ambiguous: Is this the original scalar multiplication? The vector product? The tensor product? The external product? The Clifford algebra product? These ambiguities already needs to be resolved in the documentation; where the eventual [@shadow] annotations also ought to be mentionned. Regards, octachron. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 16:13 ` octachron @ 2015-08-03 16:51 ` Daniel Bünzli 2015-08-03 17:18 ` Hendrik Boom 2015-08-03 17:59 ` octachron 0 siblings, 2 replies; 59+ messages in thread From: Daniel Bünzli @ 2015-08-03 16:51 UTC (permalink / raw) To: octachron; +Cc: Gabriel Scherer, Nils Becker, Caml-list Le lundi, 3 août 2015 à 17:13, octachron a écrit : > > The benefit is that I can understand what is happening by only looking at the expression I'm reading. With your proposal I also need to go read the library source to understand what is happening. > > I think that it is partially true. For instance, with a vector library, > "*" is always quite ambiguous: Is this the original scalar > multiplication? The vector product? The tensor product? The external > product? The Clifford algebra product? These ambiguities already needs > to be resolved in the documentation; Note that what you raise here is a different issue it's not about knowing *if* the operator is the one from M.() or the one in scope, but which one is implemented. Very often this can be disambiguised by the surrounding context and, if your operator are few (which they should be), is learnable in practice. With the operator warning splitting rule. The inferences are very simple: 1. Given a M.( * ) without warning the * may be the one of M or the one in scope. Ambiguous, can't be resolved locally. 2. Given a M.( id ) without warning, if [id] is in scope I *know* this [id] is being used. If it's not I know M.id is being used. No ambiguity, can be resolved locally. If you allow each identifier in a module to sport an @shadow annotation you lose 2. which I find a very valuable property. Without it, given that identifiers are much more widespread than operators, we get a much more ambiguous language. Best, Daniel ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 16:51 ` Daniel Bünzli @ 2015-08-03 17:18 ` Hendrik Boom 2015-08-03 17:59 ` octachron 1 sibling, 0 replies; 59+ messages in thread From: Hendrik Boom @ 2015-08-03 17:18 UTC (permalink / raw) To: caml-list On Mon, Aug 03, 2015 at 05:51:24PM +0100, Daniel Bünzli wrote: > Le lundi, 3 août 2015 à 17:13, octachron a écrit : > > > The benefit is that I can understand what is happening by only looking at the expression I'm reading. With your proposal I also need to go read the library source to understand what is happening. > > > > I think that it is partially true. For instance, with a vector library, > > "*" is always quite ambiguous: Is this the original scalar > > multiplication? The vector product? The tensor product? The external > > product? The Clifford algebra product? These ambiguities already needs > > to be resolved in the documentation; > > > Note that what you raise here is a different issue it's not about > knowing *if* the operator is the one from M.() or the one in scope, > but which one is implemented. Very often this can be disambiguised by > the surrounding context and, if your operator are few (which they > should be), is learnable in practice. > > With the operator warning splitting rule. The inferences are very > simple: > > 1. Given a M.( * ) without warning the * may be the one of M or the > one in scope. Ambiguous, can't be resolved locally. > > 2. Given a M.( id ) without warning, if [id] is in scope I *know* > this [id] is being used. If it's not I know M.id is being used. No > ambiguity, can be resolved locally. > > If you allow each identifier in a module to sport an @shadow > annotation you lose 2. which I find a very valuable property. > Without it, given that identifiers are much more widespread than > operators, we get a much more ambiguous language. It is important that simple deductions enable the reader to understand what a program means. With a statically checking compiler (what other kind is really useful) I rely on the absence of error messages when I perform these simple deductions. Therefore I prefer to either have an explicit error message or an explicit disambiguation. -- hendrik ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 16:51 ` Daniel Bünzli 2015-08-03 17:18 ` Hendrik Boom @ 2015-08-03 17:59 ` octachron 2015-08-06 13:23 ` RE : " moreno pedro 1 sibling, 1 reply; 59+ messages in thread From: octachron @ 2015-08-03 17:59 UTC (permalink / raw) To: Daniel Bünzli; +Cc: Nils Becker, Caml-list [-- Attachment #1: Type: text/plain, Size: 1521 bytes --] Le 08/03/15 18:51, Daniel Bünzli a écrit : > 1. Given a M.( * ) without warning the * may be the one of M or the one in scope. Ambiguous, can't be resolved locally. > > 2. Given a M.( id ) without warning, if [id] is in scope I*know* this [id] is being used. If it's not I know M.id is being used. No ambiguity, can be resolved locally. > > If you allow each identifier in a module to sport an @shadow annotation you lose 2. which I find a very valuable property. Without it, given that identifiers are much more widespread than operators, we get a much more ambiguous language. It is a very valid point. However, I would argue that 1. and 2. are transformed to 1. Given a M.( [edsl_keyword] ) is the one of M. If I know the EDSL keywords, there is no ambiguity. 2. Given a M.( non_keyword ) without warning, if [non_keyword] is in scope then [non_keyword] is being used. Otherwise, [M.non_keyword] is being used. No global ambiguity. This approach, contrarily to yours, has a major disadvantage: its relies on a tacit agreement on the EDSL keywords. At the same time, it allows EDSL authors to tailor the warnings to the EDSL context. If the keyword list is small/sensible enough, it might result in better warnings. But yes, implicit agreements are clearly more brittle than broad rules. A (over?)complicated solution might be to add module alias annotation in order to modify shadow annotations locally (e.g. " module N = M [@@only_shadow "+"] "). Regards, octachron. [-- Attachment #2: Type: text/html, Size: 2152 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* RE : [Caml-list] destructive local opens 2015-08-03 17:59 ` octachron @ 2015-08-06 13:23 ` moreno pedro 0 siblings, 0 replies; 59+ messages in thread From: moreno pedro @ 2015-08-06 13:23 UTC (permalink / raw) To: octachron, Daniel Bünzli; +Cc: Nils Becker, Caml-list [-- Attachment #1: Type: text/plain, Size: 1847 bytes --] Envoyé depuis un mobile Samsung -------- Message d'origine -------- De : octachron <octa@polychoron.fr> Date : 03/08/2015 19:00 (GMT+00:00) A : Daniel Bünzli <daniel.buenzli@erratique.ch> Cc : Nils Becker <nils.becker@bioquant.uni-heidelberg.de>,Caml-list <caml-list@inria.fr> Objet : Re: [Caml-list] destructive local opens Le 08/03/15 18:51, Daniel Bünzli a écrit : 1. Given a M.( * ) without warning the * may be the one of M or the one in scope. Ambiguous, can't be resolved locally. 2. Given a M.( id ) without warning, if [id] is in scope I *know* this [id] is being used. If it's not I know M.id is being used. No ambiguity, can be resolved locally. If you allow each identifier in a module to sport an @shadow annotation you lose 2. which I find a very valuable property. Without it, given that identifiers are much more widespread than operators, we get a much more ambiguous language. It is a very valid point. However, I would argue that 1. and 2. are transformed to 1. Given a M.( [edsl_keyword] ) is the one of M. If I know the EDSL keywords, there is no ambiguity. 2. Given a M.( non_keyword ) without warning, if [non_keyword] is in scope then [non_keyword] is being used. Otherwise, [M.non_keyword] is being used. No global ambiguity. This approach, contrarily to yours, has a major disadvantage: its relies on a tacit agreement on the EDSL keywords. At the same time, it allows EDSL authors to tailor the warnings to the EDSL context. If the keyword list is small/sensible enough, it might result in better warnings. But yes, implicit agreements are clearly more brittle than broad rules. A (over?)complicated solution might be to add module alias annotation in order to modify shadow annotations locally (e.g. " module N = M [@@only_shadow "+"] "). Regards, octachron. [-- Attachment #2: Type: text/html, Size: 2537 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 14:24 ` Daniel Bünzli 2015-08-03 14:37 ` Gabriel Scherer @ 2015-08-04 6:51 ` Petter Urkedal 2015-08-04 9:33 ` Goswin von Brederlow 2015-08-04 13:50 ` Hendrik Boom 2015-08-04 9:26 ` Goswin von Brederlow 2 siblings, 2 replies; 59+ messages in thread From: Petter Urkedal @ 2015-08-04 6:51 UTC (permalink / raw) To: Daniel Bünzli; +Cc: Nils Becker, Caml-list On 2015-08-03, Daniel Bünzli wrote: > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > > It's possible that people actually want M.() to mean let open! more > > often than let open. For me I think that's the case. > > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): > > let ox = V2.((dot v ox) * ox) in > V2.(3 * ox + oy) > > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. This suggests another option. If type information is available at the point this warning is emitted, then the warning could be issued only in the case when the type of the shadowing identifier matched that of the shadowed identifier. This assumes the common case for shadowing is to redefine operators or common functions at a custom type, the use of M.() being an alternative to overloading. Loosely the warning should be emitted if the chosen identifier is not the one which would have been chosen by some sensible overloading scheme, but instead we make a simple estimate. This could still go wrong, since the type required by the context may be general than the type of both the shadowed and shadowing terms, so a better rule might be to issue the warning if both are admissible in the given context, though my guess is that's harder to implement. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 6:51 ` Petter Urkedal @ 2015-08-04 9:33 ` Goswin von Brederlow 2015-08-05 6:40 ` Petter A. Urkedal 2015-08-04 13:50 ` Hendrik Boom 1 sibling, 1 reply; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-04 9:33 UTC (permalink / raw) To: caml-list On Tue, Aug 04, 2015 at 08:51:34AM +0200, Petter Urkedal wrote: > On 2015-08-03, Daniel Bünzli wrote: > > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > > > It's possible that people actually want M.() to mean let open! more > > > often than let open. For me I think that's the case. > > > > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): > > > > let ox = V2.((dot v ox) * ox) in > > V2.(3 * ox + oy) > > > > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. > > This suggests another option. If type information is available at the > point this warning is emitted, then the warning could be issued only in > the case when the type of the shadowing identifier matched that of the > shadowed identifier. > > This assumes the common case for shadowing is to redefine operators or > common functions at a custom type, the use of M.() being an alternative > to overloading. Loosely the warning should be emitted if the chosen > identifier is not the one which would have been chosen by some sensible > overloading scheme, but instead we make a simple estimate. > > This could still go wrong, since the type required by the context may be > general than the type of both the shadowed and shadowing terms, so a > better rule might be to issue the warning if both are admissible in the > given context, though my guess is that's harder to implement. I like the idea but how feasable is it? Most of the time I figure the type being infered from the operator being used. The use of M.(*) makes it the custom type while simple (*) would make it int. The type system would have to track the ambiguity until some other use of the arguments or result decide the proper type. And if it doesn't resolve then emit the warning. MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 9:33 ` Goswin von Brederlow @ 2015-08-05 6:40 ` Petter A. Urkedal 2015-08-05 10:16 ` David Allsopp 2015-08-06 9:35 ` Goswin von Brederlow 0 siblings, 2 replies; 59+ messages in thread From: Petter A. Urkedal @ 2015-08-05 6:40 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml users 2015-08-04 11:33 GMT+02:00 Goswin von Brederlow <goswin-v-b@web.de>: > On Tue, Aug 04, 2015 at 08:51:34AM +0200, Petter Urkedal wrote: >> On 2015-08-03, Daniel Bünzli wrote: >> > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : >> > > It's possible that people actually want M.() to mean let open! more >> > > often than let open. For me I think that's the case. >> > >> > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): >> > >> > let ox = V2.((dot v ox) * ox) in >> > V2.(3 * ox + oy) >> > >> > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. >> >> This suggests another option. If type information is available at the >> point this warning is emitted, then the warning could be issued only in >> the case when the type of the shadowing identifier matched that of the >> shadowed identifier. >> >> This assumes the common case for shadowing is to redefine operators or >> common functions at a custom type, the use of M.() being an alternative >> to overloading. Loosely the warning should be emitted if the chosen >> identifier is not the one which would have been chosen by some sensible >> overloading scheme, but instead we make a simple estimate. >> >> This could still go wrong, since the type required by the context may be >> general than the type of both the shadowed and shadowing terms, so a >> better rule might be to issue the warning if both are admissible in the >> given context, though my guess is that's harder to implement. > > I like the idea but how feasable is it? Most of the time I figure the > type being infered from the operator being used. The use of M.(*) > makes it the custom type while simple (*) would make it int. The type > system would have to track the ambiguity until some other use of the > arguments or result decide the proper type. And if it doesn't resolve > then emit the warning. Yes, it's probably not that easy to implement. Not familiar with the code base. At least it requires postponing the warning till type information is available, and maybe cluttering the parse tree with sub-nodes containing the types and locations of shadowed identifiers. The types at the point the check is made will have been analysed under the assumption that the right resolution was made. I originally assumed we had closed types, only type variables stemming from let-polymorphism. That may be the case for M-bound identifiers in M.(...), but the context may contain local identifiers or file-level identifiers with unresolved existentials. Nevertheless, I think the common case when we don't want to emit a warning is when M.(...) shadows a global binding whose type was already fixed. ^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: [Caml-list] destructive local opens 2015-08-05 6:40 ` Petter A. Urkedal @ 2015-08-05 10:16 ` David Allsopp 2015-08-06 9:35 ` Goswin von Brederlow 1 sibling, 0 replies; 59+ messages in thread From: David Allsopp @ 2015-08-05 10:16 UTC (permalink / raw) To: Petter A. Urkedal, Goswin von Brederlow; +Cc: caml users Petter A. Urkedal wrote: > 2015-08-04 11:33 GMT+02:00 Goswin von Brederlow <goswin-v-b@web.de>: > > On Tue, Aug 04, 2015 at 08:51:34AM +0200, Petter Urkedal wrote: > >> On 2015-08-03, Daniel Bünzli wrote: > >> > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > >> > > It's possible that people actually want M.() to mean let open! > >> > > more often than let open. For me I think that's the case. > >> > > >> > If you are in the vector case, I don't think that's the case. With Gg > [1] I often had the following kind of subtly wrong code (can't remember > the exact details but something similar): > >> > > >> > let ox = V2.((dot v ox) * ox) in > >> > V2.(3 * ox + oy) > >> > > >> > The reality is that M.() is inherently dangerous, especially from an > API evolution perspective where new identifiers with matching type may get > introduced later leading to silent semantic changes in your code. So we > should not encourage people to use M.!() as it's going to make the problem > even more acute. Besides we should have that 44 warning by default so that > we see the problems, but for now it's impossible to live with 44 and a Gg > like library. > >> > >> This suggests another option. If type information is available at > >> the point this warning is emitted, then the warning could be issued > >> only in the case when the type of the shadowing identifier matched > >> that of the shadowed identifier. > >> > >> This assumes the common case for shadowing is to redefine operators > >> or common functions at a custom type, the use of M.() being an > >> alternative to overloading. Loosely the warning should be emitted if > >> the chosen identifier is not the one which would have been chosen by > >> some sensible overloading scheme, but instead we make a simple > estimate. > >> > >> This could still go wrong, since the type required by the context may > >> be general than the type of both the shadowed and shadowing terms, so > >> a better rule might be to issue the warning if both are admissible in > >> the given context, though my guess is that's harder to implement. > > > > I like the idea but how feasable is it? Most of the time I figure the > > type being infered from the operator being used. The use of M.(*) > > makes it the custom type while simple (*) would make it int. The type > > system would have to track the ambiguity until some other use of the > > arguments or result decide the proper type. And if it doesn't resolve > > then emit the warning. > > Yes, it's probably not that easy to implement. Not familiar with the code > base. At least it requires postponing the warning till type information > is available, and maybe cluttering the parse tree with sub-nodes > containing the types and locations of shadowed identifiers. > The types at the point the check is made will have been analysed under the > assumption that the right resolution was made. I asked similar a few years ago on the list for general let-shadowing[1]. I haven't checked whether it's still working in latest ocaml/camlp4, as I vaguely recall that if-then-else shadowing isn't correctly handled in the filter, and I ended up having to disable it in the project I was using it for, but it was a reasonably short camlp4 filter and ocamllex script to do the post-processing, so I can't imagine that this similar case would be that bad in the compiler itself. I've chucked the original code at https://github.com/dra27/pf_shadow for interest. David [1] http://caml.inria.fr/pub/ml-archives/caml-list/2008/08/5b882a133318d913dd7aaa0abacf36d7.en.html ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-05 6:40 ` Petter A. Urkedal 2015-08-05 10:16 ` David Allsopp @ 2015-08-06 9:35 ` Goswin von Brederlow 1 sibling, 0 replies; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-06 9:35 UTC (permalink / raw) To: caml-list On Wed, Aug 05, 2015 at 08:40:21AM +0200, Petter A. Urkedal wrote: > 2015-08-04 11:33 GMT+02:00 Goswin von Brederlow <goswin-v-b@web.de>: > > On Tue, Aug 04, 2015 at 08:51:34AM +0200, Petter Urkedal wrote: > >> On 2015-08-03, Daniel Bünzli wrote: > >> > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > >> > > It's possible that people actually want M.() to mean let open! more > >> > > often than let open. For me I think that's the case. > >> > > >> > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): > >> > > >> > let ox = V2.((dot v ox) * ox) in > >> > V2.(3 * ox + oy) > >> > > >> > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. > >> > >> This suggests another option. If type information is available at the > >> point this warning is emitted, then the warning could be issued only in > >> the case when the type of the shadowing identifier matched that of the > >> shadowed identifier. > >> > >> This assumes the common case for shadowing is to redefine operators or > >> common functions at a custom type, the use of M.() being an alternative > >> to overloading. Loosely the warning should be emitted if the chosen > >> identifier is not the one which would have been chosen by some sensible > >> overloading scheme, but instead we make a simple estimate. > >> > >> This could still go wrong, since the type required by the context may be > >> general than the type of both the shadowed and shadowing terms, so a > >> better rule might be to issue the warning if both are admissible in the > >> given context, though my guess is that's harder to implement. > > > > I like the idea but how feasable is it? Most of the time I figure the > > type being infered from the operator being used. The use of M.(*) > > makes it the custom type while simple (*) would make it int. The type > > system would have to track the ambiguity until some other use of the > > arguments or result decide the proper type. And if it doesn't resolve > > then emit the warning. > > Yes, it's probably not that easy to implement. Not familiar with the > code base. At least it requires postponing the warning till type > information is available, and maybe cluttering the parse tree with > sub-nodes containing the types and locations of shadowed identifiers. > The types at the point the check is made will have been analysed under > the assumption that the right resolution was made. Ahh, I was thinking wrong. In M.(x + y) the type of x and y would usualy be unknown at first. The type of x and y don't matter though. Only the existance and type of M.(+) should be considered and would usualy be fixed by the module. Worst case you have to add type annotations or a signature to the module to make its API properly typed. Then when the compiler hits '+' it sees the M.(+). It then has to keep looking for the previous (+) and compare the two types. It could then not simply compare them but check if they can be unified one direction or the other. So overloading int -> int -> int with 'a -> 'a -> 'a would give the warning while int -> int -> int with Vec.t -> Vec.t -> Vec.t would not. In case of 'a adding type annotations to the operator would fix the warning. > I originally assumed we had closed types, only type variables stemming > from let-polymorphism. That may be the case for M-bound identifiers > in M.(...), but the context may contain local identifiers or > file-level identifiers with unresolved existentials. Nevertheless, I > think the common case when we don't want to emit a warning is when > M.(...) shadows a global binding whose type was already fixed. MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 6:51 ` Petter Urkedal 2015-08-04 9:33 ` Goswin von Brederlow @ 2015-08-04 13:50 ` Hendrik Boom 1 sibling, 0 replies; 59+ messages in thread From: Hendrik Boom @ 2015-08-04 13:50 UTC (permalink / raw) To: caml-list On Tue, Aug 04, 2015 at 08:51:34AM +0200, Petter Urkedal wrote: > On 2015-08-03, Daniel Bünzli wrote: > > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > > > It's possible that people actually want M.() to mean let open! more > > > often than let open. For me I think that's the case. > > > > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): > > > > let ox = V2.((dot v ox) * ox) in > > V2.(3 * ox + oy) > > > > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. > > This suggests another option. If type information is available at the > point this warning is emitted, then the warning could be issued only in > the case when the type of the shadowing identifier matched that of the > shadowed identifier. > > This assumes the common case for shadowing is to redefine operators or > common functions at a custom type, the use of M.() being an alternative > to overloading. Loosely the warning should be emitted if the chosen > identifier is not the one which would have been chosen by some sensible > overloading scheme, but instead we make a simple estimate. > > This could still go wrong, since the type required by the context may be > general than the type of both the shadowed and shadowing terms, so a > better rule might be to issue the warning if both are admissible in the > given context, though my guess is that's harder to implement. The rules for shadowing vs overloading in Algol 68 were roughly the following: If it is possible for an operators to have operands such that (considering all the available implicit type conversions) either operator could validly apply to them, then it is forbidden to define them in the same scope, and if they are defined in nested scopes, the inner declaration will shadow the outer. If it wa not possible for this ambiguity to arise, then you just had overloading. There was no additional scope-disambiguation syntax. This worked quite well in practice. Adding a shadowing-warning on everything on top of this could be a convenience for avoiding confusing code. -- hendrik ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-03 14:24 ` Daniel Bünzli 2015-08-03 14:37 ` Gabriel Scherer 2015-08-04 6:51 ` Petter Urkedal @ 2015-08-04 9:26 ` Goswin von Brederlow 2015-08-04 9:38 ` Daniel Bünzli 2 siblings, 1 reply; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-04 9:26 UTC (permalink / raw) To: caml-list On Mon, Aug 03, 2015 at 03:24:18PM +0100, Daniel Bünzli wrote: > Le lundi, 3 août 2015 à 15:08, Nils Becker a écrit : > > It's possible that people actually want M.() to mean let open! more > > often than let open. For me I think that's the case. > > If you are in the vector case, I don't think that's the case. With Gg [1] I often had the following kind of subtly wrong code (can't remember the exact details but something similar): > > let ox = V2.((dot v ox) * ox) in > V2.(3 * ox + oy) What is wrong with that code? I'm assuming V2 has: val (+): t -> t -> t val (*): int -> t -> t val dot: t -> t -> int > The reality is that M.() is inherently dangerous, especially from an API evolution perspective where new identifiers with matching type may get introduced later leading to silent semantic changes in your code. So we should not encourage people to use M.!() as it's going to make the problem even more acute. Besides we should have that 44 warning by default so that we see the problems, but for now it's impossible to live with 44 and a Gg like library. > > Best, > > Daniel > > [1] http://erratique.ch/software/gg MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 9:26 ` Goswin von Brederlow @ 2015-08-04 9:38 ` Daniel Bünzli 2015-08-04 12:26 ` vrotaru.md 2015-08-06 9:21 ` Goswin von Brederlow 0 siblings, 2 replies; 59+ messages in thread From: Daniel Bünzli @ 2015-08-04 9:38 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml-list Le mardi, 4 août 2015 à 10:26, Goswin von Brederlow a écrit : > > let ox = V2.((dot v ox) * ox) in > > V2.(3 * ox + oy) > > What is wrong with that code? > > I'm assuming V2 has: > > val (+): t -> t -> t > val (*): int -> t -> t > val dot: t -> t -> int It also has val ox : t Daniel ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 9:38 ` Daniel Bünzli @ 2015-08-04 12:26 ` vrotaru.md 2015-08-04 13:12 ` David Allsopp ` (2 more replies) 2015-08-06 9:21 ` Goswin von Brederlow 1 sibling, 3 replies; 59+ messages in thread From: vrotaru.md @ 2015-08-04 12:26 UTC (permalink / raw) To: Daniel Bünzli, Goswin von Brederlow; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 941 bytes --] After reading this thread, I'm starting to thinking about another option, namely: "local un-open", because I certainly dislike ambiguity. So, maybe, something like: Vec.( ^(3 * v) * vx + vy) where anything in ^(... ) is not subject local open. Regards În Mar, 4 aug. 2015 la 12:40, Daniel Bünzli <daniel.buenzli@erratique.ch> a scris: > Le mardi, 4 août 2015 à 10:26, Goswin von Brederlow a écrit : > > > let ox = V2.((dot v ox) * ox) in > > > V2.(3 * ox + oy) > > > > What is wrong with that code? > > > > I'm assuming V2 has: > > > > val (+): t -> t -> t > > val (*): int -> t -> t > > val dot: t -> t -> int > > It also has > > val ox : t > > Daniel > > > > -- > 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 [-- Attachment #2: Type: text/html, Size: 1682 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: [Caml-list] destructive local opens 2015-08-04 12:26 ` vrotaru.md @ 2015-08-04 13:12 ` David Allsopp 2015-08-04 13:17 ` Jeremy Yallop 2015-08-04 13:14 ` Ivan Gotovchits 2015-08-06 9:23 ` Goswin von Brederlow 2 siblings, 1 reply; 59+ messages in thread From: David Allsopp @ 2015-08-04 13:12 UTC (permalink / raw) To: vrotaru.md, Daniel Bünzli, Goswin von Brederlow; +Cc: caml-list vrotaru.md@gmail.com wrote: > After reading this thread, I'm starting to thinking about another option, > namely: "local un-open", because I certainly dislike ambiguity. > > So, maybe, something like: > > Vec.( ^(3 * v) * vx + vy) > > where anything in ^(... ) is not subject local open. Surely at this level of ambiguity-disliking, what you really need is a way of module-qualifying an infix? i.e. some alternate notation which allows you to write: 3 * v <Vec.*> vx <Vec.+> vy (my use of "<module-path.unbracketed-infix>" is entirely hypothetical - I haven't paused to consider if that would be grammatically feasible, but hopefully you can see the idea) "local un-open" and yet another interpretation of a sequence of symbols starts to sound suspiciously like we should rename the language to PerlCaml! Would that ^(...) syntax be recursive, just like local open? David ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 13:12 ` David Allsopp @ 2015-08-04 13:17 ` Jeremy Yallop 2015-08-04 13:54 ` vrotaru.md 2015-08-04 15:25 ` Drup 0 siblings, 2 replies; 59+ messages in thread From: Jeremy Yallop @ 2015-08-04 13:17 UTC (permalink / raw) To: David Allsopp Cc: vrotaru.md, Daniel Bünzli, Goswin von Brederlow, caml-list On 4 August 2015 at 14:12, David Allsopp <dra-news@metastack.com> wrote: > vrotaru.md@gmail.com wrote: > Surely at this level of ambiguity-disliking, what you really need is a way of module-qualifying an infix? i.e. some alternate notation which allows you to write: > > 3 * v <Vec.*> vx <Vec.+> vy This would certainly be useful. Haskell supports the notation 'M.op', as in v Vec.* vx and I expect the same would work in OCaml. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 13:17 ` Jeremy Yallop @ 2015-08-04 13:54 ` vrotaru.md 2015-08-04 15:25 ` Drup 1 sibling, 0 replies; 59+ messages in thread From: vrotaru.md @ 2015-08-04 13:54 UTC (permalink / raw) To: Jeremy Yallop, David Allsopp Cc: Daniel Bünzli, Goswin von Brederlow, caml-list [-- Attachment #1: Type: text/plain, Size: 308 bytes --] Re: recursive un-open There is certainly a problem with nested opens. If you module A, B and C, and every one of them defines its own (+) then.. A.( B.( C.( .... + .... ) ) ) parsing the most nested plus became quite ambiguous. But something like ^^(...) or ^^^(...) will certainly be bad [-- Attachment #2: Type: text/html, Size: 505 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 13:17 ` Jeremy Yallop 2015-08-04 13:54 ` vrotaru.md @ 2015-08-04 15:25 ` Drup 2015-08-04 22:22 ` vrotaru.md 1 sibling, 1 reply; 59+ messages in thread From: Drup @ 2015-08-04 15:25 UTC (permalink / raw) To: Jeremy Yallop, David Allsopp Cc: vrotaru.md, Daniel Bünzli, Goswin von Brederlow, caml-list Le 04/08/2015 15:17, Jeremy Yallop a écrit : > On 4 August 2015 at 14:12, David Allsopp <dra-news@metastack.com> wrote: >> vrotaru.md@gmail.com wrote: >> Surely at this level of ambiguity-disliking, what you really need is a way of module-qualifying an infix? i.e. some alternate notation which allows you to write: >> >> 3 * v <Vec.*> vx <Vec.+> vy > This would certainly be useful. Haskell supports the notation 'M.op', as in > > v Vec.* vx > > and I expect the same would work in OCaml. > I took a shot at that, I can't manage to make it work in yacc/menhir. see https://github.com/Drup/ocaml/commit/76c5a508ba1c5cff06c137c7fb67754b4e43576c The difficult case is f 1 M.+ 2 Looking at M, you can't decide if it's going to be an ident M.foo or an operator M.+ I can't manage to disambiguate that and I'm afraid it's not LR(1). ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 15:25 ` Drup @ 2015-08-04 22:22 ` vrotaru.md 2015-08-04 22:55 ` Hendrik Boom 0 siblings, 1 reply; 59+ messages in thread From: vrotaru.md @ 2015-08-04 22:22 UTC (permalink / raw) To: Drup, Jeremy Yallop, David Allsopp Cc: Daniel Bünzli, Goswin von Brederlow, caml-list [-- Attachment #1: Type: text/plain, Size: 790 bytes --] Thinking a bit more about the subject of "local un-open" I think I may not even mind a recursive variant, but with slightly different syntax, and the following interpreation: A "local un-open" undo the effect of local open, that is, no symbols from the opened module are visible. Than those can be nested with the with the obvious interpretation So with '+' defined in M, N and at the top level and ~(... ) being "local un-open" and a expression like the one below: M.( .... N.( .. + .. ~( .. + ... ~(.. + ..) .. )) first '+' would be N.+ the second one M.+ and the last the top level one. Too clumsy I guess.. Well it was fun. And I will throw in just one more possibility: explicit unopeninng of modules. ~M.(... ) - which may make sense for unshadowing symbols from Pervasives. [-- Attachment #2: Type: text/html, Size: 999 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 22:22 ` vrotaru.md @ 2015-08-04 22:55 ` Hendrik Boom 2015-08-05 4:52 ` Gabriel Scherer 0 siblings, 1 reply; 59+ messages in thread From: Hendrik Boom @ 2015-08-04 22:55 UTC (permalink / raw) To: caml-list On Tue, Aug 04, 2015 at 10:22:44PM +0000, vrotaru.md@gmail.com wrote: > Thinking a bit more about the subject of "local un-open" I think I may not > even mind a recursive variant, but with slightly different syntax, and the > following interpreation: A "local un-open" undo the effect of local open, > that is, no symbols from the opened module are visible. Than those can be > nested with the with the obvious interpretation > > So with '+' defined in M, N and at the top level and ~(... ) being "local > un-open" and a expression like the one below: > > M.( .... N.( .. + .. ~( .. + ... ~(.. + ..) .. )) > > first '+' would be N.+ the second one M.+ and the last the top level one. > > Too clumsy I guess.. Well it was fun. And I will throw in just one more > possibility: explicit unopeninng of modules. > > ~M.(... ) - which may make sense for unshadowing symbols from Pervasives. Reminds me of quoteing and unquoting in Lisps. Also generates gibberish very quickly. Not that it isn't very useful, though. -- hendrik > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 22:55 ` Hendrik Boom @ 2015-08-05 4:52 ` Gabriel Scherer 0 siblings, 0 replies; 59+ messages in thread From: Gabriel Scherer @ 2015-08-05 4:52 UTC (permalink / raw) To: Hendrik Boom; +Cc: caml users 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. On Wed, Aug 5, 2015 at 12:55 AM, Hendrik Boom <hendrik@topoi.pooq.com> wrote: > On Tue, Aug 04, 2015 at 10:22:44PM +0000, vrotaru.md@gmail.com wrote: >> Thinking a bit more about the subject of "local un-open" I think I may not >> even mind a recursive variant, but with slightly different syntax, and the >> following interpreation: A "local un-open" undo the effect of local open, >> that is, no symbols from the opened module are visible. Than those can be >> nested with the with the obvious interpretation >> >> So with '+' defined in M, N and at the top level and ~(... ) being "local >> un-open" and a expression like the one below: >> >> M.( .... N.( .. + .. ~( .. + ... ~(.. + ..) .. )) >> >> first '+' would be N.+ the second one M.+ and the last the top level one. >> >> Too clumsy I guess.. Well it was fun. And I will throw in just one more >> possibility: explicit unopeninng of modules. >> >> ~M.(... ) - which may make sense for unshadowing symbols from Pervasives. > > Reminds me of quoteing and unquoting in Lisps. Also generates > gibberish very quickly. > > Not that it isn't very useful, though. > > -- hendrik > >> >> -- >> 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 > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 12:26 ` vrotaru.md 2015-08-04 13:12 ` David Allsopp @ 2015-08-04 13:14 ` Ivan Gotovchits 2015-08-14 10:55 ` Goswin von Brederlow 2015-08-06 9:23 ` Goswin von Brederlow 2 siblings, 1 reply; 59+ messages in thread From: Ivan Gotovchits @ 2015-08-04 13:14 UTC (permalink / raw) To: vrotaru.md; +Cc: Daniel Bünzli, Goswin von Brederlow, caml-list [-- Attachment #1: Type: text/plain, Size: 1842 bytes --] I would prefer if the compiler warns us if in expression `M.(x)` the value `x` is not in the scope of `M`. Clarification: usually when we say `M.(x + cross y)` we want to use only values from module `M` and problems arise when something is captured from outside the module. Such kind of local opens are usually use to open rich modules, that fully defines an algebra of a type, i.e., a full suite of common operations like, comparison operations, arithmetics, etc. So, using local open is some kind of local algebra switch operation. In other words switching to other domain specific language. A good style would be to explicitly qualify every external value that is used inside the local opened scope: M.(x * Vec.of_array [| 1; 2 |]) On Tue, Aug 4, 2015 at 8:26 AM, vrotaru.md@gmail.com <vrotaru.md@gmail.com> wrote: > After reading this thread, I'm starting to thinking about another option, > namely: "local un-open", because I certainly dislike ambiguity. > > So, maybe, something like: > > Vec.( ^(3 * v) * vx + vy) > > where anything in ^(... ) is not subject local open. > > Regards > > > În Mar, 4 aug. 2015 la 12:40, Daniel Bünzli <daniel.buenzli@erratique.ch> > a scris: > >> Le mardi, 4 août 2015 à 10:26, Goswin von Brederlow a écrit : >> > > let ox = V2.((dot v ox) * ox) in >> > > V2.(3 * ox + oy) >> > >> > What is wrong with that code? >> > >> > I'm assuming V2 has: >> > >> > val (+): t -> t -> t >> > val (*): int -> t -> t >> > val dot: t -> t -> int >> >> It also has >> >> val ox : t >> >> Daniel >> >> >> >> -- >> 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 > > [-- Attachment #2: Type: text/html, Size: 3035 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 13:14 ` Ivan Gotovchits @ 2015-08-14 10:55 ` Goswin von Brederlow 2015-08-14 11:28 ` Drup 0 siblings, 1 reply; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-14 10:55 UTC (permalink / raw) To: caml-list On Tue, Aug 04, 2015 at 09:14:49AM -0400, Ivan Gotovchits wrote: > I would prefer if the compiler warns us if in expression `M.(x)` the value > `x` is not in the scope of `M`. > > Clarification: usually when we say `M.(x + cross y)` we want to use only > values from module `M` > and problems arise when something is captured from outside the module. Such > kind of local opens > are usually use to open rich modules, that fully defines an algebra of a > type, i.e., a full suite of > common operations like, comparison operations, arithmetics, etc. So, using > local open is some kind > of local algebra switch operation. In other words switching to other domain > specific language. > > A good style would be to explicitly qualify every external value that is > used inside the local opened scope: > > M.(x * Vec.of_array [| 1; 2 |]) 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 = M.({ x = 1; y; z = depth; }) in So I can't agree that M.() should only access values from M. Far from it. > On Tue, Aug 4, 2015 at 8:26 AM, vrotaru.md@gmail.com <vrotaru.md@gmail.com> > wrote: > > > After reading this thread, I'm starting to thinking about another option, > > namely: "local un-open", because I certainly dislike ambiguity. > > > > So, maybe, something like: > > > > Vec.( ^(3 * v) * vx + vy) > > > > where anything in ^(... ) is not subject local open. > > > > Regards > > > > > > În Mar, 4 aug. 2015 la 12:40, Daniel Bünzli <daniel.buenzli@erratique.ch> > > a scris: > > > >> Le mardi, 4 août 2015 à 10:26, Goswin von Brederlow a écrit : > >> > > let ox = V2.((dot v ox) * ox) in > >> > > V2.(3 * ox + oy) > >> > > >> > What is wrong with that code? > >> > > >> > I'm assuming V2 has: > >> > > >> > val (+): t -> t -> t > >> > val (*): int -> t -> t > >> > val dot: t -> t -> int > >> > >> It also has > >> > >> val ox : t > >> > >> Daniel MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-14 10:55 ` Goswin von Brederlow @ 2015-08-14 11:28 ` Drup 2015-08-18 11:11 ` Goswin von Brederlow 0 siblings, 1 reply; 59+ messages in thread From: Drup @ 2015-08-14 11:28 UTC (permalink / raw) To: Goswin von Brederlow, caml-list > 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 = M.({ x = 1; y; z = depth; }) in You can avoid the local open altogether and write it like that: let r = {M. x = 1; y; z = depth } in It's even shorter. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-14 11:28 ` Drup @ 2015-08-18 11:11 ` Goswin von Brederlow 2015-08-18 12:52 ` David Allsopp 0 siblings, 1 reply; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-18 11:11 UTC (permalink / raw) To: caml-list 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 = M.({ x = 1; y; z = depth; }) in > > You can avoid the local open altogether and write it like that: > > let r = {M. x = 1; y; z = depth } in > > It's even shorter. That only works because newer ocaml disambiguises (is that a word?) record labels when it determines the record type from the first label, right? So it's implicitly using M.y = y and M.z = depth. MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: [Caml-list] destructive local opens 2015-08-18 11:11 ` Goswin von Brederlow @ 2015-08-18 12:52 ` David Allsopp 2015-08-18 13:00 ` Gabriel Scherer 0 siblings, 1 reply; 59+ messages in thread From: David Allsopp @ 2015-08-18 12:52 UTC (permalink / raw) To: Goswin von Brederlow, caml-list 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 = M.({ x = 1; y; z = depth; }) in > > > > You can avoid the local open altogether and write it like that: > > > > let r = {M. x = 1; y; z = 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 = y and M.z = 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 by Americans! But this isn't related to the disambiguation features of OCaml 4.01+. Those allow you to write things like: type t = {x : int} type u = {x : int; y : string} let foo = {x = 1} let bar = {x = 42; y = ""} 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 = 1; M.y = y; M.z = 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 needed to put the module path on one label (and it doesn't have to be the 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} to mean {y = 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-18 12:52 ` David Allsopp @ 2015-08-18 13:00 ` Gabriel Scherer 2015-08-18 22:26 ` Anthony Tavener 0 siblings, 1 reply; 59+ messages in thread From: Gabriel Scherer @ 2015-08-18 13:00 UTC (permalink / raw) To: David Allsopp; +Cc: Goswin von Brederlow, caml-list [-- Attachment #1: Type: text/plain, Size: 2276 bytes --] 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 <dra-news@metastack.com> 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 = M.({ x = 1; y; z = depth; }) in > > > > > > You can avoid the local open altogether and write it like that: > > > > > > let r = {M. x = 1; y; z = 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 = y and M.z = 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 by Americans! > > But this isn't related to the disambiguation features of OCaml 4.01+. > Those allow you to write things like: > > type t = {x : int} > type u = {x : int; y : string} > > let foo = {x = 1} > let bar = {x = 42; y = ""} > > 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 = 1; M.y = y; M.z = 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 needed to put > the module path on one label (and it doesn't have to be the 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} > to mean {y = 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 > [-- Attachment #2: Type: text/html, Size: 3319 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-18 13:00 ` Gabriel Scherer @ 2015-08-18 22:26 ` Anthony Tavener 2015-08-19 13:55 ` Oleg ` (2 more replies) 0 siblings, 3 replies; 59+ messages in thread From: Anthony Tavener @ 2015-08-18 22:26 UTC (permalink / raw) To: Gabriel Scherer; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 6402 bytes --] (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 = 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 = 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 also 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 have 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 <dra-news@metastack.com> > 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 = M.({ x = 1; y; z = depth; }) in >> > > >> > > You can avoid the local open altogether and write it like that: >> > > >> > > let r = {M. x = 1; y; z = 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 = y and M.z = 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 by Americans! >> >> But this isn't related to the disambiguation features of OCaml 4.01+. >> Those allow you to write things like: >> >> type t = {x : int} >> type u = {x : int; y : string} >> >> let foo = {x = 1} >> let bar = {x = 42; y = ""} >> >> 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 = 1; M.y = y; M.z = 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 needed >> to put the module path on one label (and it doesn't have to be the 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} >> to mean {y = 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 >> > > [-- Attachment #2: Type: text/html, Size: 8715 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-18 22:26 ` Anthony Tavener @ 2015-08-19 13:55 ` Oleg 2015-08-19 14:13 ` John Whitington 2015-08-19 15:52 ` Hendrik Boom 2015-08-19 15:55 ` Simon Cruanes 2 siblings, 1 reply; 59+ messages in thread From: Oleg @ 2015-08-19 13:55 UTC (permalink / raw) To: gabriel.scherer; +Cc: caml-list [Since this discussion still continues, I think I will comment on Gabriel's comment after all.] "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." Actually there have been proposals to go into that direction, about 30 years ago. http://research.microsoft.com/lampson/39-Pebble/39-PebbleAbstract.htm Please look at p 287 of the paper: ``Pebble also has an anti-LET, which impoverishes the environment instead of enriching it.'' This is exactly the issue under discussion in this thread. [It is amazingly that the paper tackles the issues we are still struggling with today, including dependent types. It reads surprisingly modern...] There is an independent motivation for restricting the environment: syntactic conditional independence. If two stochastic expressions share no variables, the are independent and conditionally independent. Alas, this syntactic test -- which David McAllister likes very much [see his presentation at the workshop on probabilistic programming at NIPS 2008] -- has limited applicability since many expressions are in the scope of many let-bindings (even though a particular expression depends on a fraction of all the bindings in scope). It would be great to have a construct to remove bindings from scope, to strengthen so to speak. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-19 13:55 ` Oleg @ 2015-08-19 14:13 ` John Whitington 2015-08-19 15:47 ` Hendrik Boom 0 siblings, 1 reply; 59+ messages in thread From: John Whitington @ 2015-08-19 14:13 UTC (permalink / raw) To: Oleg, gabriel.scherer, caml-list Hi, Oleg wrote: > [Since this discussion still continues, I think I will comment on > Gabriel's comment after all.] > > "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." > > Actually there have been proposals to go into that direction, about 30 > years ago. > > http://research.microsoft.com/lampson/39-Pebble/39-PebbleAbstract.htm > > Please look at p 287 of the paper: ``Pebble also has an anti-LET, > which impoverishes the environment instead of enriching it.'' This is > exactly the issue under discussion in this thread. I've sometimes thought it would be nice to be able to write something like: let x = f a b c and y = g d e in let x' for x y = x * 2 + y in (x', h x') where "let x' for x y" introduces the binding of x' and takes x and y out of scope. Now, if we accidentally write... let x = f a b c and y = g d e in let x' for x, y = x * 2 + y in (x', h x) ...we would get an error, since x is out of scope on the last line. You could imagine also "let x' for _ = ... in ..." which removes all variables in the current scope used in the calculation of x' from scope, which could be determined syntactically. This seems draconian, though. John -- John Whitington Director, Coherent Graphics Ltd http://www.coherentpdf.com/ ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-19 14:13 ` John Whitington @ 2015-08-19 15:47 ` Hendrik Boom 0 siblings, 0 replies; 59+ messages in thread From: Hendrik Boom @ 2015-08-19 15:47 UTC (permalink / raw) To: caml-list On Wed, Aug 19, 2015 at 03:13:34PM +0100, John Whitington wrote: > Hi, > > Oleg wrote: > >[Since this discussion still continues, I think I will comment on > >Gabriel's comment after all.] > > > > "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." > > > >Actually there have been proposals to go into that direction, about 30 > >years ago. > > > > http://research.microsoft.com/lampson/39-Pebble/39-PebbleAbstract.htm > > > >Please look at p 287 of the paper: ``Pebble also has an anti-LET, > >which impoverishes the environment instead of enriching it.'' This is > >exactly the issue under discussion in this thread. > > I've sometimes thought it would be nice to be able to write something like: > > let x = f a b c > and y = g d e in > let x' for x y = x * 2 + y in > (x', h x') > > where "let x' for x y" introduces the binding of x' and takes x and > y out of scope. Now, if we accidentally write... > > let x = f a b c > and y = g d e in > let x' for x, y = x * 2 + y in > (x', h x) > > ...we would get an error, since x is out of scope on the last line. This is very explicit about what it allows and shadows. I like it. > You could imagine also "let x' for _ = ... in ..." which removes all > variables in the current scope used in the calculation of x' from > scope, which could be determined syntactically. This seems > draconian, though. > This is possibly less useful. > John > > -- > John Whitington > Director, Coherent Graphics Ltd > http://www.coherentpdf.com/ > > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-18 22:26 ` Anthony Tavener 2015-08-19 13:55 ` Oleg @ 2015-08-19 15:52 ` Hendrik Boom 2015-08-19 18:09 ` Anthony Tavener 2015-08-19 15:55 ` Simon Cruanes 2 siblings, 1 reply; 59+ messages in thread From: Hendrik Boom @ 2015-08-19 15:52 UTC (permalink / raw) To: caml-list On Tue, Aug 18, 2015 at 04:26:00PM -0600, Anthony Tavener wrote: > (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 = > M.(init (flag1 + flag2 + flags)) (* flags is intended to be 'local', > but it could be shadowed by a value in M *) The way to make this explicit is to write let init flags = (M.init (M.flag1 + M.flag2 + flags) (* flags is 'local', and is not shadowed by a value in M *) THe code is one characte longer, and even the comment becomes smaller! -- hendrik ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-19 15:52 ` Hendrik Boom @ 2015-08-19 18:09 ` Anthony Tavener 0 siblings, 0 replies; 59+ messages in thread From: Anthony Tavener @ 2015-08-19 18:09 UTC (permalink / raw) To: Hendrik Boom; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 2025 bytes --] On Wed, Aug 19, 2015 at 9:52 AM, Hendrik Boom <hendrik@topoi.pooq.com> wrote: > > The way to make this explicit is to write > > let init flags = > (M.init (M.flag1 + M.flag2 + flags) (* flags is 'local', and is not > shadowed by a value in M *) > > THe code is one characte longer, and even the comment becomes smaller! > > The '+' operator is also from M, not just normal addition. And, instead of M, it might be "Sdl.Init". :) Local open has been fantastic for code hygene and clarity -- previously it was very common to define local module aliases (even though they weren't actual aliases at the time!): module S = Sdl.Init... and then prefixing everything with S, similar to your example. But this is ugly, still redundant, plus it adds cognitive indirection (alias). Often when I use a local open it's in an expression which uses mostly values from the opened module, with a few from local scope, very much like the example I gave. If it's not heavily from the external module, of course I only prefix the relevant identifiers rather than opening scope. I've always hated the following function-call sloppiness in several languages: Namespace::func( Namespace::arg1, Namespace::Enum::arg2, myArg ); I felt like the arguments should be implicitly open to the namespace of the called function. Then local-open in OCaml, particularly with the short syntax M.(), provided an elegant solution -- without need for anything implicit! I facepalmed that day. Module.(func arg1 Enum.arg2) myarg Fantabulous! Now the minor quibble to this otherwise luxurious syntax is that there is no way to make it clear when something really is local rather than coming from an opened scope -- and this arises when currying doesn't pan out as nice as the above, or more often: once binary operators are involved. But as I said, there has been the related (at least conceptually) thorn of the inability to specify the path of the current module. I'm hoping someone familiar with OCaml internals and style has a bright idea. [-- Attachment #2: Type: text/html, Size: 2902 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-18 22:26 ` Anthony Tavener 2015-08-19 13:55 ` Oleg 2015-08-19 15:52 ` Hendrik Boom @ 2015-08-19 15:55 ` Simon Cruanes 2015-08-19 16:42 ` Arthur Wendling 2015-08-19 21:15 ` octachron 2 siblings, 2 replies; 59+ messages in thread From: Simon Cruanes @ 2015-08-19 15:55 UTC (permalink / raw) To: Anthony Tavener, Gabriel Scherer; +Cc: caml-list [-- Attachment #1: Type: text/plain, Size: 7615 bytes --] 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 and 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.>>= 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ût 2015 00:26:00 UTC+02:00, Anthony Tavener <anthony.tavener@gmail.com> a écrit : >(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 = > 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 = >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 >also >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 >have >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 ><dra-news@metastack.com> >> 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 = M.({ x = 1; y; z = depth; }) in >>> > > >>> > > You can avoid the local open altogether and write it like that: >>> > > >>> > > let r = {M. x = 1; y; z = 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 = y and M.z = 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 by Americans! >>> >>> But this isn't related to the disambiguation features of OCaml >4.01+. >>> Those allow you to write things like: >>> >>> type t = {x : int} >>> type u = {x : int; y : string} >>> >>> let foo = {x = 1} >>> let bar = {x = 42; y = ""} >>> >>> 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 = 1; M.y = y; M.z = 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 >needed >>> to put the module path on one label (and it doesn't have to be the >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} >>> to mean {y = 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 >>> >> >> > >-- >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 [-- Attachment #2: Type: text/html, Size: 9857 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-19 15:55 ` Simon Cruanes @ 2015-08-19 16:42 ` Arthur Wendling 2015-08-19 21:15 ` octachron 1 sibling, 0 replies; 59+ messages in thread From: Arthur Wendling @ 2015-08-19 16:42 UTC (permalink / raw) To: Simon Cruanes; +Cc: Anthony Tavener, Gabriel Scherer, caml-list [-- Attachment #1: Type: text/plain, Size: 10464 bytes --] 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 = (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 = (V2 for dot, ( * )).((dot v ox) * ox) in (V2 for ( * ), ( + )).(3 * ox + oy) let open (V2 for dot, ( + ), ( * )) in let ox = (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 <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 demonstrated by the proposal of new warnings and > 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.>>= 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ût 2015 00:26:00 UTC+02:00, Anthony Tavener < > anthony.tavener@gmail.com> a écrit : >> >> (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 = >> 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 = >> 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 also >> 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 have >> 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 <dra-news@metastack.com> >>> 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 = M.({ x = 1; y; z = depth; }) in >>>> > > >>>> > > You can avoid the local open altogether and write it like that: >>>> > > >>>> > > let r = {M. x = 1; y; z = 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 = y and M.z = 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 by Americans! >>>> >>>> But this isn't related to the disambiguation features of OCaml 4.01+. >>>> Those allow you to write things like: >>>> >>>> type t = {x : int} >>>> type u = {x : int; y : string} >>>> >>>> let foo = {x = 1} >>>> let bar = {x = 42; y = ""} >>>> >>>> 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 = 1; M.y = y; M.z = 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 needed >>>> to put the module path on one label (and it doesn't have to be the 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} to mean {y = 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 > [-- Attachment #2: Type: text/html, Size: 12958 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-19 15:55 ` Simon Cruanes 2015-08-19 16:42 ` Arthur Wendling @ 2015-08-19 21:15 ` octachron 2015-08-20 8:06 ` Romain Bardou 1 sibling, 1 reply; 59+ messages in thread From: octachron @ 2015-08-19 21:15 UTC (permalink / raw) To: caml-list [-- Attachment #1: Type: text/plain, Size: 2120 bytes --] From my point of view, a prefixed notation M.+ does not replace all use case of local opens, for at least two reasons: 1. It makes operators harder to spot since I can not rely any more on the first character 2. It adds syntactic noise and thus decreases readability within the DSL As an illustration, a simple rotation (** Rotation on the vect(x,y) plane with an angle t precondition: x and y are orthonormal *) let rotation (x,y) t v = let open V in v + R.( ( cos t - 1. ) * (v|*|x) - sin t * (v|*|y) ) * x + R.( sin t * (v|*|x) + ( cos t - 1. ) * (v|*|y) ) * y (where V is a vector module, R the associated real-like field and |*| the scalar product) becomes let rotation (x,y) t v = v V.+ ( ( cos t R.- 1. ) R.* ( v V.|*| x ) R.- sin t R.* ( v V.|*| y ) ) V.* x V.+ ( sin t R.* ( v V.|*| x ) R.+ ( cos t R.- 1. ) R.* ( v V.|*| y ) ) V.* y with your proposition. In this second version, I have a hard time separating the different subexpressions, and I don't think it would improve much with a better familiarity with the syntax. At the same time, I do agree that it would be nice to be able to use operators as operators without having to bring them in the local scope. -- octachron Le 08/19/15 17:55, Simon Cruanes a écrit : > 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 and 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.>>= if it removes ambiguity and potential shadowing > bugs. Of course I don't know if this is compatible with the current > syntax. > > -- > Simon [-- Attachment #2: Type: text/html, Size: 3006 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-19 21:15 ` octachron @ 2015-08-20 8:06 ` Romain Bardou 2015-08-20 17:03 ` Yotam Barnoy 0 siblings, 1 reply; 59+ messages in thread From: Romain Bardou @ 2015-08-20 8:06 UTC (permalink / raw) To: caml-list So basically we have to write more stuff to be able to write less stuff. Unless the "write more stuff" part can be factored there is no point. The best place to factor is in libraries, and we're back to annotating libraries, something that Daniel pointed out was not ideal either. It seems to me that trying to tackle this issue from a syntax perspective will not yield good results, and that one should seek help from the type-checker instead. Basically we want some form of overloading. Type classes would solve a lot of use cases. OCaml does provide typing-based disambiguation though, although that is only for records and sum types and not for values. What if we could use infix symbols for sum types? Let's assume for a moment that (!) and (+) are valid constructors. module Int = struct type t = | (!) of int | (+) of t * t end module Float = struct type t = | (!) of float | (+) of t * t end let x: Int.t = !42 + !59 let y: Float.t = !42. + !59. (If you replace ! by A and + by B this is a valid OCaml program.) Compare the last two lines with: let x = Int.(!42 + !59) let y = Float.(!42. + !59.) The character count is similar but in the first example there is less risk of shadowing, at the cost of having to disable warning 40. I personally believe that disambiguation is cleaner than local open. Also, in more complex examples the first solution may not even need the type annotation, such as in: type t = { x: Int.t; y: Float.t } (* disambiguation-based example *) let _ = { x = !42 + !59; y = !42. + !59. } (* local-open-based example *) let _ = { x = Int.(!42 + !59); y = Float.(!42. + !59.) } OCaml's disambiguation is kind of reversed type classes: disambiguation propagates type constraints top-down whereas type classes propagates type constraints bottom-up. The latter is more powerful - in particular typing can be principal - but it requires much more from the type system. On 19/08/2015 23:15, octachron wrote: > From my point of view, a prefixed notation M.+ does not replace all use > case of local opens, for at least two reasons: > > 1. It makes operators harder to spot since I can not rely any more > on the first character > 2. It adds syntactic noise and thus decreases readability within > the DSL > > As an illustration, a simple rotation > > (** Rotation on the vect(x,y) plane with an angle t > precondition: x and y are orthonormal *) > let rotation (x,y) t v = let open V in > v > + R.( ( cos t - 1. ) * (v|*|x) - sin t * (v|*|y) ) * x > + R.( sin t * (v|*|x) + ( cos t - 1. ) * (v|*|y) ) * y > > (where V is a vector module, R the associated real-like field and |*| > the scalar product) > becomes > > let rotation (x,y) t v = > v > V.+ ( ( cos t R.- 1. ) R.* ( v V.|*| x ) R.- sin t R.* ( v V.|*| > y ) ) V.* x > V.+ ( sin t R.* ( v V.|*| x ) R.+ ( cos t R.- 1. ) R.* ( v V.|*| > y ) ) V.* y > > with your proposition. > > In this second version, I have a hard time separating the different > subexpressions, and I don't think it would improve much with a better > familiarity with the syntax. > > At the same time, I do agree that it would be nice to be able to use > operators as operators without having to bring them in the local scope. > > -- octachron > > Le 08/19/15 17:55, Simon Cruanes a écrit : >> 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 and 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.>>= if it removes ambiguity and potential shadowing >> bugs. Of course I don't know if this is compatible with the current >> syntax. >> >> -- >> Simon > ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-20 8:06 ` Romain Bardou @ 2015-08-20 17:03 ` Yotam Barnoy 2015-08-20 19:19 ` Erkki Seppala 0 siblings, 1 reply; 59+ messages in thread From: Yotam Barnoy @ 2015-08-20 17:03 UTC (permalink / raw) To: Romain Bardou; +Cc: Ocaml Mailing List [-- Attachment #1: Type: text/plain, Size: 9016 bytes --] A lot of great ideas here. I want to comment on many of them, so please bear with me while I summarize some of the points in the process. Hopefully this will also help anybody new to the discussion, which has become extremely long. - Local opens are somewhat of an anti-pattern in OCaml, because they're usually used in places where you have the same names defined in multiple modules: (* both M and N are in other files, and they define (+). M also defines (*) *) M.(let x = foo + bar in N.(y + z * x)) The first + uses M's implementation and the second uses N's implementation. The problem is, if you ever change one of the modules to include another function that wasn't expected originally (for example, N now includes *), you now have subtle bugs breaking your code in completely separate files from the ones you were editing, and the type system can't necessarily do anything to catch these errors. - Similar languages to OCaml (such as Haskell) outlaw having the same name defined twice in the same scope. OCaml only issues a warning. IMO, this warning should be turned into an error. However, these other languages also provide a way to use the same name in the same scope by having type-based dispatch or disambiguation. For example, haskell has typeclasses. OCaml currently doesn't have this. I think this shows that Modular Implicits aren't just a nice-to-have feature -- we really need them to be able to get to do the kinds of things other languages do safely. - I personally don't think the splitting of the warning between alphanumerics and operators is that helpful. This isn't a warning you should ever turn off IMO, regardless of the domain. - The destructive versions of local opens which disable these warnings are even worse anti-patterns, since they're just a huge recipe for bugs. - In the absence of Modular Implicits, as Petter mentioned, it would be nice to have the warnings/potential errors only generated when the same names have the same types. In case of different types with the same name, the type system should take care of any issues. For example, if M's (*) has a different type than N's (*), there's no problem per se since we'll get a type error. I think this is definitely something worth pursuing. - In general, it would be nice not to have to open all these local scopes, and rather, as Simon mentioned, to reference operators directly when needed (as in, M.+). The problem is that these cannot be used infix in the code due to parser reasons. I think that while we're looking for a parsing solution for this, it might be better to also look for a parsing solution for turning any name into an infix function, and simply apply that. Haskell uses backtick (` as in x `mod` y), which we already use for polymorphic variants. Can we think of another symbol with which to do this? We could then apply it to M.+ as well. - Another good point is that it would be nice to limit our imports of a module in the same file in which it is used. The issue is that module syntax is too verbose, due to the fact that it requires types. However, our module type signatures could possibly drop the type definitions, relying on imported type definitions instead. Something like the following would be nice: module M : sig val (+) val (-) val mult end = LongModuleName Note that this is similar to haskell's qualified import statement, but is more powerful since it creates a local module. This is also easy to parse since val is a keyword. The dropped types would form holes in the signature, which must be made available in the referenced module. When wanting to do multi-level local opens, rather than opening modules blindly, you'd first create smaller submodules locally, and then only open those locally. This would be much safer, and is in fact much closer to the original point of having multiple possible type signature 'views' into a module implementation (which is nice in theory but is not used much except for functors and artificial illustrations of OCaml's abilities). - Another point that was brought up was that it would be nice to access the local scope of the current module, which I think is extremely useful. How about _.foo as a possible syntax? -Yotam On Thu, Aug 20, 2015 at 1:06 AM, Romain Bardou <romain.bardou@inria.fr> wrote: > So basically we have to write more stuff to be able to write less stuff. > Unless the "write more stuff" part can be factored there is no point. The > best place to factor is in libraries, and we're back to annotating > libraries, something that Daniel pointed out was not ideal either. > > It seems to me that trying to tackle this issue from a syntax perspective > will not yield good results, and that one should seek help from the > type-checker instead. > > Basically we want some form of overloading. Type classes would solve a lot > of use cases. OCaml does provide typing-based disambiguation though, > although that is only for records and sum types and not for values. What if > we could use infix symbols for sum types? Let's assume for a moment that > (!) and (+) are valid constructors. > > module Int = > struct > type t = > | (!) of int > | (+) of t * t > end > > module Float = > struct > type t = > | (!) of float > | (+) of t * t > end > > let x: Int.t = !42 + !59 > let y: Float.t = !42. + !59. > > (If you replace ! by A and + by B this is a valid OCaml program.) > > Compare the last two lines with: > > let x = Int.(!42 + !59) > let y = Float.(!42. + !59.) > > The character count is similar but in the first example there is less risk > of shadowing, at the cost of having to disable warning 40. I personally > believe that disambiguation is cleaner than local open. Also, in more > complex examples the first solution may not even need the type annotation, > such as in: > > type t = { x: Int.t; y: Float.t } > > (* disambiguation-based example *) > let _ = { x = !42 + !59; y = !42. + !59. } > > (* local-open-based example *) > let _ = { x = Int.(!42 + !59); y = Float.(!42. + !59.) } > > OCaml's disambiguation is kind of reversed type classes: disambiguation > propagates type constraints top-down whereas type classes propagates type > constraints bottom-up. The latter is more powerful - in particular typing > can be principal - but it requires much more from the type system. > > > On 19/08/2015 23:15, octachron wrote: > >> From my point of view, a prefixed notation M.+ does not replace all use >> case of local opens, for at least two reasons: >> >> 1. It makes operators harder to spot since I can not rely any more >> on the first character >> 2. It adds syntactic noise and thus decreases readability within >> the DSL >> >> As an illustration, a simple rotation >> >> (** Rotation on the vect(x,y) plane with an angle t >> precondition: x and y are orthonormal *) >> let rotation (x,y) t v = let open V in >> v >> + R.( ( cos t - 1. ) * (v|*|x) - sin t * (v|*|y) ) * x >> + R.( sin t * (v|*|x) + ( cos t - 1. ) * (v|*|y) ) * y >> >> (where V is a vector module, R the associated real-like field and |*| >> the scalar product) >> becomes >> >> let rotation (x,y) t v = >> v >> V.+ ( ( cos t R.- 1. ) R.* ( v V.|*| x ) R.- sin t R.* ( v V.|*| >> y ) ) V.* x >> V.+ ( sin t R.* ( v V.|*| x ) R.+ ( cos t R.- 1. ) R.* ( v V.|*| >> y ) ) V.* y >> >> with your proposition. >> >> In this second version, I have a hard time separating the different >> subexpressions, and I don't think it would improve much with a better >> familiarity with the syntax. >> >> At the same time, I do agree that it would be nice to be able to use >> operators as operators without having to bring them in the local scope. >> >> -- octachron >> >> Le 08/19/15 17:55, Simon Cruanes a écrit : >> >>> 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 and 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.>>= if it removes ambiguity and potential shadowing >>> bugs. Of course I don't know if this is compatible with the current >>> syntax. >>> >>> -- >>> Simon >>> >> >> > > -- > 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 > [-- Attachment #2: Type: text/html, Size: 10494 bytes --] ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-20 17:03 ` Yotam Barnoy @ 2015-08-20 19:19 ` Erkki Seppala 0 siblings, 0 replies; 59+ messages in thread From: Erkki Seppala @ 2015-08-20 19:19 UTC (permalink / raw) To: caml-list Hi, Couple points I felt the need to address :). Yotam Barnoy <yotambarnoy@gmail.com> writes: > A lot of great ideas here. I want to comment on many of them, so > please bear with me while I summarize some of the points in the > process. Hopefully this will also help anybody new to the discussion, > which has become extremely long. > > - Local opens are somewhat of an anti-pattern in OCaml, because they're > usually used in places where you have the same names defined in > multiple modules: While this is certainly a very popular use case in particular when dealing with operators, I find myself using the local 'let open' form very often in other contexts. In fact, I don't ever seem to find myself using it for redefining identifiers. Examples I grepped from my sources, admittedly some obsoleted by the new disambiguation feature: 2014/device-table/layout.ml: let open Containers_misc.PrintBox in hlist ~bars:false [box; text (String.make n ' ')] 2014/TinyGUploader/src/upload.ml: let open Gcode.Parser in match input with | Move { move_reg = (G0 | G1); move_pos = at } -> .. 2014/TinyGUploader/src/upload.ml: let open ANSITerminal in let progress = float n /. float input_nlines in move_bol (); let time_left = (Unix.gettimeofday () -. t0) /. progress in let time_finished = t0 +. time_left in if not common_options.co_verbose then ( printf [Bold; green; on_default] "%2.1f%%" (100.0 *. progress); printf [default] "complete, %d/%d, ETA " n input_nlines; printf [Bold; green; on_default] "%s (%s)" (Utils.human_eta (int_of_float time_left)) (Utils.string_of_time time_finished); erase Eol; ) 2014/TinyGUploader/src/align.ml: let render_gcode cairo mapping_matrix gcode = let open Cairo in set_line_width cairo (length_in_matrix mapping_matrix 3.0); set_line_cap cairo ROUND; set_line_join cairo JOIN_ROUND; .. 2014/TinyGUploader/src/align.ml: let open Bigarray in let open Array1 in let ba = create int8_unsigned c_layout (String.length str) in for c = 0 to String.length str - 1 do ba.{c} <- Char.code str.[c] done; ba Many of these would be more verbose without using a local open, the only non-verbose alternate being opening the modules for the whole remaining module. Using the local open gives the person reading the code a heavy hint that this code will be dealing a lot with the concepts of that module. > The problem is, if you ever change one of the modules to include > another function that wasn't expected originally (for example, N now > includes *), you now have subtle bugs breaking your code in completely > separate files from the ones you were editing, and the type system > can't necessarily do anything to catch these errors. I think this is an instance where theory and practice don't match: in practice, I don't ever remember having witnessed such a bug. > - Similar languages to OCaml (such as Haskell) outlaw having the same > name defined twice in the same scope. OCaml only issues a warning. > IMO, this warning should be turned into an error. I don't think these is such a warning in general. I referred to http://caml.inria.fr/pub/docs/manual-ocaml/comp.html . Do you mean only definitions, not expression scopes? I in fact find it quite convenient to be able to write: let furbulation = acquire_value () in let furbulation, brightness = process_step_1 furbulation in let furbulation, volume = process_step_2 (brightness / 2) furbulation in .. The alternative of renaming intermediate steps can result in mistakes if the order of steps is to be changed, as can happen when doing signal processing algorithms in an explorative fashion. Cheers, -- _____________________________________________________________________ / __// /__ ____ __ http://www.modeemi.fi/~flux/\ \ / /_ / // // /\ \/ / \ / /_/ /_/ \___/ /_/\_\@modeemi.fi \/ ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 12:26 ` vrotaru.md 2015-08-04 13:12 ` David Allsopp 2015-08-04 13:14 ` Ivan Gotovchits @ 2015-08-06 9:23 ` Goswin von Brederlow 2 siblings, 0 replies; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-06 9:23 UTC (permalink / raw) To: caml-list On Tue, Aug 04, 2015 at 12:26:11PM +0000, vrotaru.md@gmail.com wrote: > After reading this thread, I'm starting to thinking about another option, > namely: "local un-open", because I certainly dislike ambiguity. > > So, maybe, something like: > > Vec.( ^(3 * v) * vx + vy) > > where anything in ^(... ) is not subject local open. > > Regards In that the '*' oeprator would not come from Vec, which would make it fail. And: Vec.( 3 * ^(v) * ^(vx) + ^(vy)) Looks horrible. MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-04 9:38 ` Daniel Bünzli 2015-08-04 12:26 ` vrotaru.md @ 2015-08-06 9:21 ` Goswin von Brederlow 2015-08-06 10:19 ` Daniel Bünzli 1 sibling, 1 reply; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-06 9:21 UTC (permalink / raw) To: caml-list On Tue, Aug 04, 2015 at 10:38:44AM +0100, Daniel Bünzli wrote: > Le mardi, 4 août 2015 à 10:26, Goswin von Brederlow a écrit : > > > let ox = V2.((dot v ox) * ox) in > > > V2.(3 * ox + oy) > > > > What is wrong with that code? > > > > I'm assuming V2 has: > > > > val (+): t -> t -> t > > val (*): int -> t -> t > > val dot: t -> t -> int > > It also has > > val ox : t > > Daniel Whatever for? You should think up better names for your constants and singletons that say what they are and are reasonably save from being accidentally used. "ox" tells me nothing about what that might be and 1 and 2 letter names are definetly only for local and temporary bindings. MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-06 9:21 ` Goswin von Brederlow @ 2015-08-06 10:19 ` Daniel Bünzli 2015-08-06 13:36 ` Hendrik Boom 0 siblings, 1 reply; 59+ messages in thread From: Daniel Bünzli @ 2015-08-06 10:19 UTC (permalink / raw) To: Goswin von Brederlow; +Cc: caml-list Le jeudi, 6 août 2015 à 10:21, Goswin von Brederlow a écrit : > > It also has > > > > val ox : t > > > > Daniel > Whatever for? It could have been `e1`, `ex`, `i`, all these names are used in the mathematical literature to stand for the unit vector on the x-axis. As you can see all these names are short and there is absolutely no point in inventing something special just to avoid *your* dogma that two letter identifiers should only be used for local identifiers. Identifiers should match the vocabulary of the domain you are dealing with. Daniel ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-06 10:19 ` Daniel Bünzli @ 2015-08-06 13:36 ` Hendrik Boom 2015-08-14 10:57 ` Goswin von Brederlow 0 siblings, 1 reply; 59+ messages in thread From: Hendrik Boom @ 2015-08-06 13:36 UTC (permalink / raw) To: caml-list On Thu, Aug 06, 2015 at 11:19:08AM +0100, Daniel Bünzli wrote: > Le jeudi, 6 août 2015 à 10:21, Goswin von Brederlow a écrit : > > > It also has > > > > > > val ox : t > > > > > > Daniel > > Whatever for? > > It could have been `e1`, `ex`, `i`, all these names are used in the mathematical literature to stand for the unit vector on the x-axis. As you can see all these names are short and there is absolutely no point in inventing something special just to avoid *your* dogma that two letter identifiers should only be used for local identifiers. Identifiers should match the vocabulary of the domain you are dealing with. Just as in <math.h> in C, which defined y0 and y1. -- hendrik ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-06 13:36 ` Hendrik Boom @ 2015-08-14 10:57 ` Goswin von Brederlow 0 siblings, 0 replies; 59+ messages in thread From: Goswin von Brederlow @ 2015-08-14 10:57 UTC (permalink / raw) To: caml-list On Thu, Aug 06, 2015 at 09:36:41AM -0400, Hendrik Boom wrote: > On Thu, Aug 06, 2015 at 11:19:08AM +0100, Daniel Bünzli wrote: > > Le jeudi, 6 août 2015 à 10:21, Goswin von Brederlow a écrit : > > > > It also has > > > > > > > > val ox : t > > > > > > > > Daniel > > > Whatever for? > > > > It could have been `e1`, `ex`, `i`, all these names are used in the mathematical literature to stand for the unit vector on the x-axis. As you can see all these names are short and there is absolutely no point in inventing something special just to avoid *your* dogma that two letter identifiers should only be used for local identifiers. Identifiers should match the vocabulary of the domain you are dealing with. > > Just as in <math.h> in C, which defined y0 and y1. > > -- hendrik And still we have not learned from it. :) MfG Goswin ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens @ 2015-08-17 10:17 Nils Becker 2015-08-17 14:26 ` Gabriel Scherer 0 siblings, 1 reply; 59+ messages in thread From: Nils Becker @ 2015-08-17 10:17 UTC (permalink / raw) To: caml-list Sorry for the late comment. The mentioned bug: > let ox = V2.((dot v ox) * ox) in > V2.(3 * ox + oy) because V2 also has V2.ox sure is nasty. But the same would happen with a local 'let open V2'. let ox = let open V2 in (dot v ox) * ox in V2.(3 * ox + oy) For ease of reasoning I think it may be a good idea to make M.(...) and M.!(...) behave in exactly the same way as let open and let open! respectively, including error messages. This would probably forbid distinguishing between infix operators and the rest. (Admittedly, in the latter example one would be tempted to combine the local opens here which would make things clearer) > The reality is that M.() is inherently dangerous, When one uses a (destructive) local open one trades the possibility of ambiguity for conciseness. In a few cases the conciseness is really, really important, so that this trade-off is worth it. (I'm thinking about math.) The existence of let open! in the language says that there is some consensus that ambiguity is sometimes acceptable. > The benefit is that I can understand what is happening by only > looking at the > expression I'm reading. With your proposal I also need to go read > the library > source to understand what is happening. It's not that bad. Destructive opens mean one has to know the shadowing module's signature. Only the values exported in the signature actually shadow anything (I just tried it out) so one does _not_ need to know the module source! Right now, one cannot really use M.(...) to shadow without deactivating warning 44. This is not ideal. With the syntax M.!(...) one would be forced to declare shadowing intent and 44 could be switched on. Then let ox = V2.!((dot v ox) * ox) in V2.!(3 * ox + oy) would still let the same bug happen but at least one was forced to write an exclamation mark which should give a feeling of "be careful about shadowing here!". Would this not be enough? n. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-17 10:17 Nils Becker @ 2015-08-17 14:26 ` Gabriel Scherer 2015-08-17 15:11 ` Nils Becker 0 siblings, 1 reply; 59+ messages in thread From: Gabriel Scherer @ 2015-08-17 14:26 UTC (permalink / raw) To: Nils Becker; +Cc: caml users > For ease of reasoning I think it may be a good idea to make M.(...) and > M.!(...) behave in exactly the same way as let open and let open! > respectively, including error messages. This would probably forbid > distinguishing between infix operators and the rest. Indeed, I don't think anyone planned to have (let open{,!} M in ...) behave differently from M.{!,}(...). In fact I don't think there is any distinction between those two constructions at the level of typing derivations (except maybe as on-the-side annotation for pretty-printing), so it is natural to handle them in exactly the same way. I proposed an implementation of the distinction between operators and alphanumeric identifiers in Shadow warnings: 44 refined into 52,53 for alphanumeric identifiers and symbolic operators https://github.com/ocaml/ocaml/pull/232 and this is orthogonal to the question of whether to add M.!(...), for which a patch was proposed by Gabriel Radanne in Short syntax for overriding local open https://github.com/ocaml/ocaml/pull/218 I am of the opinion that, whether we decide to introduce M.!(...) or not (this looks like a good idea for consistency), both (let open!) and M.!(...) are dangerous constructions that should be avoided whenever possible, and that we could and should drastically reduce the number of situations where the user is encouraged to use them. (Another idea I had was to write !<pattern>, which would have the same semantics as <pattern>, except that it claims that the identifiers bound in <pattern> shadow identifiers in scope. I find this syntax cute, but the symmetry with ((!) : 'a ref -> 'a) is troubling in a bad way.) On Mon, Aug 17, 2015 at 12:17 PM, Nils Becker <nils.becker@bioquant.uni-heidelberg.de> wrote: > Sorry for the late comment. > > The mentioned bug: > >> let ox = V2.((dot v ox) * ox) in >> V2.(3 * ox + oy) > > because V2 also has V2.ox sure is nasty. But the same would happen with > a local 'let open V2'. > > let ox = > let open V2 in > (dot v ox) * ox in > V2.(3 * ox + oy) > > For ease of reasoning I think it may be a good idea to make M.(...) and > M.!(...) behave in exactly the same way as let open and let open! > respectively, including error messages. This would probably forbid > distinguishing between infix operators and the rest. > > (Admittedly, in the latter example one would be tempted to combine the > local opens here which would make things clearer) > >> The reality is that M.() is inherently dangerous, > > When one uses a (destructive) local open one trades the possibility of > ambiguity for conciseness. In a few cases the conciseness is really, > really important, so that this trade-off is worth it. (I'm thinking > about math.) The existence of let open! in the language says that there > is some consensus that ambiguity is sometimes acceptable. > > >> The benefit is that I can understand what is happening by only >> looking at the >> expression I'm reading. With your proposal I also need to go read >> the library >> source to understand what is happening. > > It's not that bad. Destructive opens mean one has to know the shadowing > module's signature. Only the values exported in the signature actually > shadow anything (I just tried it out) so one does _not_ need to know the > module source! > > Right now, one cannot really use M.(...) to shadow without deactivating > warning 44. This is not ideal. With the syntax M.!(...) one would be > forced to declare shadowing intent and 44 could be switched on. Then > > let ox = V2.!((dot v ox) * ox) in > V2.!(3 * ox + oy) > > would still let the same bug happen but at least one was forced to write > an exclamation mark which should give a feeling of "be careful about > shadowing here!". Would this not be enough? > > > n. > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-17 14:26 ` Gabriel Scherer @ 2015-08-17 15:11 ` Nils Becker 2015-08-17 15:17 ` Drup 2015-08-17 15:18 ` Gabriel Scherer 0 siblings, 2 replies; 59+ messages in thread From: Nils Becker @ 2015-08-17 15:11 UTC (permalink / raw) To: caml-list On 17/08/15 16:26, Gabriel Scherer wrote: > both (let open!) > and M.!(...) are dangerous constructions that should be avoided > whenever possible, and that we could and should drastically reduce the > number of situations where the user is encouraged to use them. This may have been brought up before, but one way to discourage would be to make it very verbose. One would require both (let open!) or M.!() at the shadowing site and an extra annotation for each identifier or operator in M's signature that is supposed to shadow. The signature would then have to carry that information around. Then M.() would never shadow, M.!() indicates possibility of shadowing and by looking at the type of M one can see which of M's values are actually shadowing? A tool like merlin could then even annotate this graphically. but this may be too drastic a change... n. ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-17 15:11 ` Nils Becker @ 2015-08-17 15:17 ` Drup 2015-08-17 15:18 ` Gabriel Scherer 1 sibling, 0 replies; 59+ messages in thread From: Drup @ 2015-08-17 15:17 UTC (permalink / raw) To: Nils Becker, caml-list > This may have been brought up before, but one way to discourage would be > to make it very verbose. I'm sorry, but discouraging things by verbosity is a terrible idea. If you want people not to use something, Just give them something better (which is what gabriel scherer's PR is doing). (on top of that, in this case, it breaks compat) ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-17 15:11 ` Nils Becker 2015-08-17 15:17 ` Drup @ 2015-08-17 15:18 ` Gabriel Scherer 2015-08-17 18:31 ` Hendrik Boom 1 sibling, 1 reply; 59+ messages in thread From: Gabriel Scherer @ 2015-08-17 15:18 UTC (permalink / raw) To: Nils Becker; +Cc: caml users Note that forcing you to use "let open! M" instead of M.!(...) is already a way to make you pay. On Mon, Aug 17, 2015 at 5:11 PM, Nils Becker <nils.becker@bioquant.uni-heidelberg.de> wrote: > On 17/08/15 16:26, Gabriel Scherer wrote: >> both (let open!) >> and M.!(...) are dangerous constructions that should be avoided >> whenever possible, and that we could and should drastically reduce the >> number of situations where the user is encouraged to use them. > > This may have been brought up before, but one way to discourage would be > to make it very verbose. One would require both (let open!) or M.!() at > the shadowing site and an extra annotation for each identifier or > operator in M's signature that is supposed to shadow. The signature > would then have to carry that information around. > > Then M.() would never shadow, M.!() indicates possibility of shadowing > and by looking at the type of M one can see which of M's values are > actually shadowing? A tool like merlin could then even annotate this > graphically. > > but this may be too drastic a change... > > n. > > -- > 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 ^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: [Caml-list] destructive local opens 2015-08-17 15:18 ` Gabriel Scherer @ 2015-08-17 18:31 ` Hendrik Boom 0 siblings, 0 replies; 59+ messages in thread From: Hendrik Boom @ 2015-08-17 18:31 UTC (permalink / raw) To: caml-list On Mon, Aug 17, 2015 at 05:18:13PM +0200, Gabriel Scherer wrote: > Note that forcing you to use "let open! M" instead of M.!(...) is > already a way to make you pay. You shouldn't have to pay to use a feature. Verbosity is appropriate for a rarely used feature that you could otherwise easily use by accident without noticing it. And someone reading the code should then also notice it. -- hendrik ^ permalink raw reply [flat|nested] 59+ messages in thread
end of thread, other threads:[~2015-08-20 19:19 UTC | newest] Thread overview: 59+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-08-03 13:39 [Caml-list] destructive local opens Nils Becker 2015-08-03 13:43 ` Thomas Refis 2015-08-03 13:45 ` Daniel Bünzli 2015-08-03 13:47 ` Daniel Bünzli [not found] ` <55BF75F6.1040006@bioquant.uni-heidelberg.de> 2015-08-03 14:24 ` Daniel Bünzli 2015-08-03 14:37 ` Gabriel Scherer 2015-08-03 14:43 ` Daniel Bünzli 2015-08-03 15:10 ` octachron 2015-08-03 15:22 ` Daniel Bünzli 2015-08-03 16:13 ` octachron 2015-08-03 16:51 ` Daniel Bünzli 2015-08-03 17:18 ` Hendrik Boom 2015-08-03 17:59 ` octachron 2015-08-06 13:23 ` RE : " moreno pedro 2015-08-04 6:51 ` Petter Urkedal 2015-08-04 9:33 ` Goswin von Brederlow 2015-08-05 6:40 ` Petter A. Urkedal 2015-08-05 10:16 ` David Allsopp 2015-08-06 9:35 ` Goswin von Brederlow 2015-08-04 13:50 ` Hendrik Boom 2015-08-04 9:26 ` Goswin von Brederlow 2015-08-04 9:38 ` Daniel Bünzli 2015-08-04 12:26 ` vrotaru.md 2015-08-04 13:12 ` David Allsopp 2015-08-04 13:17 ` Jeremy Yallop 2015-08-04 13:54 ` vrotaru.md 2015-08-04 15:25 ` Drup 2015-08-04 22:22 ` vrotaru.md 2015-08-04 22:55 ` Hendrik Boom 2015-08-05 4:52 ` Gabriel Scherer 2015-08-04 13:14 ` Ivan Gotovchits 2015-08-14 10:55 ` Goswin von Brederlow 2015-08-14 11:28 ` Drup 2015-08-18 11:11 ` Goswin von Brederlow 2015-08-18 12:52 ` David Allsopp 2015-08-18 13:00 ` Gabriel Scherer 2015-08-18 22:26 ` Anthony Tavener 2015-08-19 13:55 ` Oleg 2015-08-19 14:13 ` John Whitington 2015-08-19 15:47 ` Hendrik Boom 2015-08-19 15:52 ` Hendrik Boom 2015-08-19 18:09 ` Anthony Tavener 2015-08-19 15:55 ` Simon Cruanes 2015-08-19 16:42 ` Arthur Wendling 2015-08-19 21:15 ` octachron 2015-08-20 8:06 ` Romain Bardou 2015-08-20 17:03 ` Yotam Barnoy 2015-08-20 19:19 ` Erkki Seppala 2015-08-06 9:23 ` Goswin von Brederlow 2015-08-06 9:21 ` Goswin von Brederlow 2015-08-06 10:19 ` Daniel Bünzli 2015-08-06 13:36 ` Hendrik Boom 2015-08-14 10:57 ` Goswin von Brederlow 2015-08-17 10:17 Nils Becker 2015-08-17 14:26 ` Gabriel Scherer 2015-08-17 15:11 ` Nils Becker 2015-08-17 15:17 ` Drup 2015-08-17 15:18 ` Gabriel Scherer 2015-08-17 18:31 ` Hendrik Boom
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox