> My personal suggestion on the proposal would be to use `$` as you've said

> Either way, let's not lose the momentum on this!

I'm working on a prototype and proposal for exactly that as we speak, but 
for the sake of momentum of the prototype, repurposed the capture operator 
in my initial experimentation. Perhaps I should have framed this as "new 
feature: tagged-variable captures" *first*, and mentioned "overloading the 
capture operator" *as a followup* extension of the proposal *second*, 
instead of the other way around. I got excited by having a working 
prototype with the existing operator I suppose:D
On Wednesday, June 28, 2023 at 7:50:59 PM UTC-5 zachary....@gmail.com wrote:

> Perhaps removing the `:` is a bad idea actually? I see now it removes the 
> consistency on values. Either way, let's not lose the momentum on this! I 
> feel like we can get this over the line (or put the last nails in its 
> coffin for good )
>
>
> On Wed, Jun 28, 2023 at 8:45 PM, Zach Daniel <zachary....@gmail.com> 
> wrote:
>
>> I agree with Paul on the specific operator, however I feel like you've 
>> just done a great thing laying out most if not all of the considerations 
>> we've had on this conversation to date. I know that these conversations can 
>> get long and sometimes not produce fruit, but I feel like we should try to 
>> chase this down and come to a conclusion on the matter if at all possible.
>>
>> My personal suggestion on the proposal would be to use `$` as you've 
>> said, and to remove the need for `:` for the case of atoms.
>>
>> %{$foo, $bar} = %{foo: 10, bar: 10}
>>
>> %{$"foo", $"bar"} = map
>>
>> It is a new operator, but it feels expressive to me, and the $ currently 
>> has no use in mainstream elixir syntax (its used in ets match specs as a 
>> value, not as an operator). That seems like a good solution to me.
>>
>>
>> On Wed, Jun 28, 2023 at 8:45 PM, Christopher Keele <
>> christheke...@gmail.com> wrote:
>>
>>> > My thoughts on the proposal itself aside, I’d just like to say that I 
>>> think you’ve set a great example of what proposals on this list should look 
>>> like. Well done!
>>>
>>> Much appreciated!
>>>
>>> > I have an almost visceral reaction to the use of capture syntax for 
>>> this though.
>>>
>>> > I think calling the `&…` syntax “capture syntax” is actually 
>>> misleading, and only has that name because it can be used to construct 
>>> closures by “capturing” a function name, but it is more accurate to 
>>> consider it closure syntax, in my opinion.
>>>
>>> This is a very salient point. How do you feel about introducing a new 
>>> operator for this sugar, such as $:foo?
>>> On Wednesday, June 28, 2023 at 7:41:05 PM UTC-5 Paul Schoenfelder wrote:
>>>
>>>> My thoughts on the proposal itself aside, I’d just like to say that I 
>>>> think you’ve set a great example of what proposals on this list should 
>>>> look 
>>>> like. Well done!
>>>>
>>>> I have an almost visceral reaction to the use of capture syntax for 
>>>> this though, and I don’t believe any of the languages you mentioned that 
>>>> support field punning do so in this fashion. They all use a similar 
>>>> intuitive syntax where the variable matches the field name, and they don’t 
>>>> make any effort to support string keys.
>>>>
>>>> If Elixir is to ever support field punning, I strongly believe it 
>>>> should follow their example. However, there are reasons why Elixir cannot 
>>>> do so due to syntax ambiguities (IIRC). In my mind, that makes any effort 
>>>> to introduce this feature a non-starter, because code should be first and 
>>>> foremost easy to read, and I have yet to see a proposal for this that 
>>>> doesn’t make the code harder to read and understand, including this one.
>>>>
>>>> I’d like to have field punning, but by addressing, if possible, the 
>>>> core issue that is blocking it. If that can’t be done, I just don’t think 
>>>> the cost of overloading unrelated syntax is worth it. I think calling the 
>>>> `&…` syntax “capture syntax” is actually misleading, and only has that 
>>>> name 
>>>> because it can be used to construct closures by “capturing” a function 
>>>> name, but it is more accurate to consider it closure syntax, in my 
>>>> opinion. 
>>>> Overloading it to mean capturing things in a more general sense will be 
>>>> confusing for everyone, and would only work in a few restricted forms, 
>>>> which makes it more difficult to teach and learn.
>>>>
>>>> That’s my two cents anyway, I think you did a great job with the 
>>>> proposal, but I’m very solidly against it as the solution to the problem 
>>>> being solved.
>>>>
>>>> Paul
>>>>
>>>>
>>>>
>>>> On Wed, Jun 28, 2023, at 7:56 PM, Christopher Keele wrote:
>>>>
>>>> This is a formalization of my concept here 
>>>> <https://groups.google.com/g/elixir-lang-core/c/oFbaOT7rTeU/m/BWF24zoAAgAJ>,
>>>>  
>>>> as a first-class proposal for explicit discussion/feedback, since I now 
>>>> have a working prototype 
>>>> <https://github.com/elixir-lang/elixir/compare/main...christhekeele:elixir:tagged-variable-capture>
>>>> .
>>>>
>>>> *Goal*
>>>>
>>>> The aim of this proposal is to support a commonly-requested feature: 
>>>> *short-hand 
>>>> construction and pattern matching of key/value pairs of associative data 
>>>> structures, based on variable names* in the current scope.
>>>>
>>>> *Context*
>>>>
>>>> Similar shorthand syntax sugar exists in many programming languages 
>>>> today, known variously as:
>>>>
>>>>    - Field Punning <https://dev.realworldocaml.org/records.html> — 
>>>>    OCaml
>>>>    - Record Puns 
>>>>    
>>>> <https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/record_puns.html> 
>>>>    — Haskell
>>>>    - Object Property Value Shorthand 
>>>>    
>>>> <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#property_definitions>
>>>>  
>>>>    — ES6 Javascript
>>>>    
>>>> This feature has been in discussion for a decade, on this mailing list (
>>>> 1 
>>>> <https://groups.google.com/g/elixir-lang-core/c/4w9eOeLvt-8/m/WOkoPSMm6kEJ>,
>>>>  
>>>> 2 
>>>> <https://groups.google.com/g/elixir-lang-core/c/NoUo2gqQR3I/m/WTpArTGMKSIJ>,
>>>>  
>>>> 3 
>>>> <https://groups.google.com/g/elixir-lang-core/c/3XrVXEVSixc/m/NHU2M4QFAQAJ>,
>>>>  
>>>> 4 
>>>> <https://groups.google.com/g/elixir-lang-core/c/OvSQkvXxsmk/m/bKKHbBxiCwAJ>,
>>>>  
>>>> 5 
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/1W-d_XAlBgAJ>
>>>> , 6 <https://groups.google.com/g/elixir-lang-core/c/oFbaOT7rTeU>) 
>>>> and the Elixir forum (1 
>>>> <https://elixirforum.com/t/proposal-add-field-puns-map-shorthand-to-elixir/15452>,
>>>>  
>>>> 2 
>>>> <https://elixirforum.com/t/shorthand-for-passing-variables-by-name/30583>, 
>>>> 3 
>>>> <https://elixirforum.com/t/if-you-could-change-one-thing-in-elixir-language-what-you-would-change/19902/17>,
>>>>  
>>>> 4 
>>>> <https://elixirforum.com/t/has-map-shorthand-syntax-in-other-languages-caused-you-any-problems/15403>,
>>>>  
>>>> 5 
>>>> <https://elixirforum.com/t/es6-ish-property-value-shorthands-for-maps/1524>,
>>>>  
>>>> 6 
>>>> <https://elixirforum.com/t/struct-creation-pattern-matching-short-hand/7544>),
>>>>  
>>>> and has motivated many libraries (1 
>>>> <https://github.com/whatyouhide/short_maps>, 2 
>>>> <https://github.com/meyercm/shorter_maps>, 3 
>>>> <https://hex.pm/packages/shorthand>, 4 <https://hex.pm/packages/synex>). 
>>>> These narrow margins cannot fit the full history of possibilities, 
>>>> proposals, and problems with this feature, and I will not attempt to 
>>>> summarize them all. For context, I suggest reading this mailing list 
>>>> proposal 
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/1W-d_XAlBgAJ>
>>>>  
>>>> and this community discussion 
>>>> <https://elixirforum.com/t/proposal-add-field-puns-map-shorthand-to-elixir/15452>
>>>>  in 
>>>> particular.
>>>>
>>>> However, in summary, this particular proposal tries to solve a couple 
>>>> of past sticking points:
>>>>
>>>>    1. Atom vs String 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/NoUo2gqQR3I/m/IpZQHbZk4xEJ>
>>>>  
>>>>    key support
>>>>    2. Visual clarity 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/NBkAVto0BAAJ>
>>>>  
>>>>    that atom/string matching is occurring
>>>>    3. Limitations of string-based sigil parsing 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/TiZw6xM3BAAJ>
>>>>    4. Easy confusion 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/WRhXxHDfBAAJ>
>>>>  
>>>>    with tuples
>>>>    
>>>> I have a working fork of Elixir here 
>>>> <https://github.com/christhekeele/elixir/tree/tagged-variable-capture> 
>>>> where this proposed syntax can be experimented with. Be warned, it is 
>>>> buggy.
>>>>
>>>> *Proposal: Tagged Variable Captures*
>>>>
>>>> I propose we overload the unary capture operator (*&*) to accept 
>>>> compile-time atoms and strings as arguments, for example *&:foo* and 
>>>> *&"bar"*. This would *expand at compile time* into *a tagged tuple 
>>>> with the atom/string and a variable reference*. For now, I am calling 
>>>> this a *"tagged-variable capture"*  to differentiate it from a 
>>>> function capture.
>>>>
>>>> For the purposes of this proposal, assume:
>>>>
>>>> {foo, bar} = {1, 2}
>>>>
>>>> Additionally,
>>>>
>>>>    - Lines beginning with *# == * indicate what the compiler expands 
>>>>    an expression to.
>>>>    - Lines beginning with *# => * represent the result of evaluating 
>>>>    that expression.
>>>>    - Lines beginning with *# !> * represent an exception.
>>>>    
>>>> *Bare Captures*
>>>>
>>>> I'm not sure if we should support *bare* tagged-variable capture, but 
>>>> it is illustrative for this proposal, so I left it in my prototype. It 
>>>> would look like:
>>>>
>>>> &:foo
>>>> *# == **{:foo, foo}*
>>>> *# => *{:foo, 1}
>>>> &"foo"
>>>> *# == **{"foo", foo}*
>>>> *# => *{"foo", 1}
>>>>
>>>> If bare usage is supported, this expansion would work as expected in 
>>>> match and guard contexts as well, since it expands before variable 
>>>> references are resolved:
>>>>
>>>> {:foo, baz} = &:foo
>>>> *# == {:foo, baz} = {:foo, foo}*
>>>> *# => *{:foo, 1}
>>>> baz
>>>> *# => *1
>>>>
>>>> *List Captures*
>>>>
>>>> Since capture expressions are allowed in lists, this can be used to 
>>>> construct Keyword lists from the local variable scope elegantly:
>>>>
>>>> list = [&:foo, &:bar]
>>>> *# == **list = [{:foo, foo}, {:bar, bar}]*
>>>> *# => *[foo: 1, bar: 2]
>>>>
>>>> This would work with other list operators like *|*:
>>>>
>>>> baz = 3
>>>> list = [&:baz | list]
>>>> *# == **list = [**{:baz, baz} **| **list**]*
>>>> *# => *[baz: 3, foo: 1, bar: 2]
>>>>
>>>> And list destructuring:
>>>>
>>>> {foo, bar, baz} = {nil, nil, nil}
>>>> [&:baz, &:foo, &:bar] = list
>>>> *# == [{:baz, baz}, {:foo, foo}, {:bar, bar}] = list*
>>>> *# => *[baz: 3, foo: 1, bar: 2]
>>>> {foo, bar, baz}
>>>> *# => *{1, 2, 3}
>>>>
>>>> *Map Captures*
>>>>
>>>> With a small change to the parser, 
>>>> <https://github.com/elixir-lang/elixir/commit/0a4f5376c0f9b4db7d71514d05df6b8b6abc96a9>
>>>>  
>>>> we can allow this expression inside map literals. Because this expression 
>>>> individually gets expanded into a tagged-tuple before the map associations 
>>>> list as a whole are processed, it allow this syntax to work in all 
>>>> existing 
>>>> map/struct constructs, like map construction:
>>>>
>>>> map = %{&:foo, &"bar"}
>>>> *# == %{:foo => foo, "bar" => bar}*
>>>> *# => *%{:foo => 1, "bar" => 2}
>>>>
>>>> Map updates:
>>>>
>>>> foo = 3
>>>> map = %{map | &:foo}
>>>> *# == %{map | :foo => foo}*
>>>> *# => *%{:foo => 3, "bar" => 2}
>>>>
>>>> And map destructuring:
>>>>
>>>> {foo, bar} = {nil, nil}
>>>> %{&:foo, &"bar"} = map
>>>> *# == %{:foo => foo, "bar" => bar} = map*
>>>> *# => *%{:foo => 3, "bar" => 2}
>>>> {foo, bar}
>>>> *# => *{3, 2}
>>>>
>>>> *Considerations*
>>>>
>>>> Though just based on an errant thought 
>>>> <https://groups.google.com/g/elixir-lang-core/c/oFbaOT7rTeU/m/BWF24zoAAgAJ>
>>>>  
>>>> that popped into my head yesterday, I'm unreasonably pleased with how well 
>>>> this works and reads in practice. I will present my thoughts here, though 
>>>> again I encourage you to grab my branch 
>>>> <https://github.com/christhekeele/elixir/tree/tagged-variable-capture>, 
>>>> compile it from source 
>>>> <https://github.com/christhekeele/elixir/tree/tagged-variable-capture#compiling-from-source>,
>>>>  and 
>>>> play with it yourself!
>>>>
>>>> *Pro: solves existing pain points*
>>>>
>>>> As mentioned, this solves flaws previous proposals suffer from:
>>>>
>>>>    1. Atom vs String 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/NoUo2gqQR3I/m/IpZQHbZk4xEJ>
>>>>  key 
>>>>    support
>>>>    This supports both.
>>>>    2. Visual clarity 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/NBkAVto0BAAJ>
>>>>  that 
>>>>    atom/string matching is occurring
>>>>    This leverages the appropriate literal in question within the 
>>>>    syntax sugar.
>>>>    3. Limitations of string-based sigil parsing 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/TiZw6xM3BAAJ>
>>>>    This is compiler-expansion-native.
>>>>    4. Easy confusion 
>>>>    
>>>> <https://groups.google.com/g/elixir-lang-core/c/XxnrGgZsyVc/m/WRhXxHDfBAAJ>
>>>>  with 
>>>>    tuples
>>>>    %{&:foo, &"bar"} is very different from {foo, bar}, instead of 
>>>>    1-character different.
>>>>    
>>>> Additionally, it solves my main complaint with historical proposals: 
>>>> syntax to combine a variable identifier with a literal must either obscure 
>>>> that we are building an identifier, or obscure the key/string typing of 
>>>> the 
>>>> literal.
>>>>
>>>> I'm proposing overloading the capture operator rather than introducing 
>>>> a new operator because the capture operator already has a semantic 
>>>> association with messing with variable scope, via the nested integer-based 
>>>> positional function argument syntax (ex *& &1*).
>>>>
>>>> By using the capture operator we indicate that we are messing with an 
>>>> identifier in scope, but via a literal atom/string we want to associate 
>>>> with, to get the best of both worlds.
>>>>
>>>> *Pro: works with existing code*
>>>>
>>>> The capture today operator has well-defined compile-time-error 
>>>> semantics if you try to pass it an atom or a string. All compiling Elixir 
>>>> code today will continue to compile as before.
>>>>
>>>> *Pro: works with existing tooling*
>>>>
>>>> By overloading an existing operator, this approach works seamlessly for 
>>>> me with the syntax highlighters I have tried it with so far, and 
>>>> reasonable 
>>>> with the formatter.
>>>>
>>>> In my experimentation I've found that the formatter wants to rewrite 
>>>> *&:baz 
>>>> *to *(&:baz)* pretty often. That's good, because there are several 
>>>> edge cases in my prototype where not doing so causes it to behave 
>>>> strangely; I'm sure it's resolving ambiguities that would occur in 
>>>> function 
>>>> captures that impact my proposal in ways I have yet fully anticipated.
>>>>
>>>> *Pros: minimizes surface area of the language*
>>>>
>>>> By overriding the capture operator instead of introducing a new 
>>>> operator or sigil, we are able to keep the surface area of this feature 
>>>> slim.
>>>>
>>>> *Cons: overloads the capture operator*
>>>>
>>>> Of course, much of the virtues of this proposal comes from overloading 
>>>> the capture operator. But it is an already semantically fraught syntactic 
>>>> sugar construct that causes confusion to newcomers, and this would place 
>>>> more strain on it.
>>>>
>>>> We would need to augment it with more than the meager error message 
>>>> modification 
>>>> <https://github.com/elixir-lang/elixir/commit/3d83d21ada860d03cece8c6f90dbcf7bf9e737ec#diff-92b98063d1e86837fae15261896c265ab502b8d556141aaf1c34e67a3ef3717cL199-R207>
>>>>  in 
>>>> my prototype, as well as documentation and anticipate a new wave of 
>>>> questions from the community upon release.
>>>>
>>>> This inelegance really shows when considering embedding a tagged 
>>>> variable capture inside an anonymous function capture, ex *& &1 = 
>>>> &:foo*. In my prototype I've chosen to allow this rather than error on 
>>>> "nested captures not allowed" (would probably become: "nested 
>>>> *function* captures not allowed"), but I'm not sure I found all the 
>>>> edge-cases of mixing them in all possible constructions.
>>>>
>>>> Additionally, since my proposal now allows the capture operator as an 
>>>> associative element inside map literal parsing, that would change the 
>>>> syntax error reported by providing a function capture as an associative 
>>>> element to be generated during expansion rather than during parsing. I am 
>>>> not fluent enough in leex to have have updated the parser to preserve the 
>>>> exact old error, but serendipitously what it reports in my prototype 
>>>> today is pretty good regardless, but I prefer the old behaviour:
>>>>
>>>> Old:
>>>> %{& &1}
>>>> *# !> **** (SyntaxError) syntax error before '}'*
>>>> *# !> * |
>>>> *# !> * 1 | %{& &1}
>>>> *# !> * | ^
>>>> New:
>>>> %{& &1}
>>>> *# => error: expected key-value pairs in a map, got: & &1*
>>>> *# => ** (CompileError) cannot compile code (errors have been logged)*
>>>>
>>>> *Cons: here there be dragons I cannot see*
>>>>
>>>> I'm quite sure a full implementation would require a lot more knowledge 
>>>> of the compiler than I am able to provide. For example, *&:foo = &:foo 
>>>> *raises an exception where *(&:foo) = &:foo* behaves as expected. I 
>>>> also find the variable/context/binding environment implementation in the 
>>>> erlang part of the compiler during expansion to be impenetrable, and I'm 
>>>> sure my prototype fails on edge cases there.
>>>>
>>>> *Open Question: the pin operator*
>>>>
>>>> As this feature constructs a variable ref for you, it is not clear 
>>>> if/how we should support attempts to pin the generated variable to avoid 
>>>> new bindings. In my prototype, I have tried to support the pin operator 
>>>> via 
>>>> the *&^:atom *syntax, though I'm pretty sure it's super buggy on bare 
>>>> out-of-data-structure cases and I only got it far enough to work in 
>>>> function heads for basic function head map pattern matching.
>>>>
>>>> *Open Question: charlists*
>>>>
>>>> I did not add support for charlist tagged variable captures in my 
>>>> prototype, as it would be more involved to differentiate a capture of list 
>>>> mean to become a tagged tuple from a list representing the AST of a 
>>>> function capture. I would not lose a lot of sleep over this.
>>>>
>>>> *Open Question: allowed contexts*
>>>>
>>>> Would we even want to allow this syntax construct outside of map 
>>>> literals? Or list literals?
>>>>
>>>> I can certainly see people abusing the 
>>>> bare-outside-of-associative-datastructure syntax to make some neigh 
>>>> impenetrable code where it's really unclear where assignment and pattern 
>>>> matching is occuring, and relatedly this is where I see a lot of odd 
>>>> edge-case behaviour in my prototype. I allowed it to speed up the 
>>>> implementation, but it merits more discussion.
>>>>
>>>> On the other hand, this does seem like an... interesting use-case:
>>>>
>>>> error = "rate limit exceeded"
>>>> &:error *# return error tuple*
>>>>
>>>> *Thanks for reading! What do you think?*
>>>>
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google 
>>>> Groups "elixir-lang-core" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send 
>>>> an email to elixir-lang-co...@googlegroups.com.
>>>> To view this discussion on the web visit 
>>>> https://groups.google.com/d/msgid/elixir-lang-core/ad7e0313-4207-4cb7-a5f3-d824735830abn%40googlegroups.com
>>>>  
>>>> <https://groups.google.com/d/msgid/elixir-lang-core/ad7e0313-4207-4cb7-a5f3-d824735830abn%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>> .
>>>>
>>>>
>>>> -- 
>>> You received this message because you are subscribed to the Google 
>>> Groups "elixir-lang-core" group.
>>> To unsubscribe from this group and stop receiving emails from it, send 
>>> an email to elixir-lang-core+unsubscr...@googlegroups.com.
>>> To view this discussion on the web visit 
>>> https://groups.google.com/d/msgid/elixir-lang-core/2b46232e-04f1-4b21-87e6-9c098741cd36n%40googlegroups.com
>>>  
>>> <https://groups.google.com/d/msgid/elixir-lang-core/2b46232e-04f1-4b21-87e6-9c098741cd36n%40googlegroups.com?utm_medium=email&utm_source=footer>
>>> .
>>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/07aeda87-2b71-4db1-8fad-6d60c0c3d3c2n%40googlegroups.com.

Reply via email to