caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Re: [Caml-list] destructive local opens
@ 2015-08-17 10:17 Nils Becker
  2015-08-17 14:26 ` Gabriel Scherer
  0 siblings, 1 reply; 58+ 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] 58+ messages in thread

* Re: [Caml-list] destructive local opens
  2015-08-17 10:17 [Caml-list] destructive local opens Nils Becker
@ 2015-08-17 14:26 ` Gabriel Scherer
  2015-08-17 15:11   ` Nils Becker
  0 siblings, 1 reply; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* 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; 58+ 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] 58+ 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; 58+ 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] 58+ messages in thread

* Re: [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
  2015-08-03 13:47   ` Daniel Bünzli
  1 sibling, 1 reply; 58+ 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] 58+ messages in thread

* Re: [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
  1 sibling, 0 replies; 58+ 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] 58+ messages in thread

* [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; 58+ 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] 58+ messages in thread

end of thread, other threads:[~2015-08-20 19:19 UTC | newest]

Thread overview: 58+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-17 10:17 [Caml-list] destructive local opens 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
  -- strict thread matches above, loose matches on Subject: below --
2015-08-03 13:39 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-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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).