Apologies, Google Groups did something truly awful with the formatting on 
some of those example blocks, but I'm not messing with them further as it 
takes a lot of time to fake the coloration of a VSCode editing experience 
in them :)
On Friday, December 20, 2024 at 5:37:00 PM UTC-6 Christopher Keele wrote:

> Had a new idea, so necro'ing this thread with another proposal for a 
> "tagged variable capture" syntax, this time by overloading another existing 
> operator.
>
> *Problems addressed from previous proposal*
>
>    - The overloading the capture operator was an unpopular choice (ie &:foo 
>    # => {:foo, foo})
>    - A new operator adds syntactic overhead to the language
>    - The more popular choice for new operator, $ (dollar sign), conflicts 
>    with the popular expectation that future type system improvements may want 
>    to use it
>    - Of the other remaining new operator choices, 
>       - ` (tilde)'s association with "unquoting" in LISPs kind of makes 
>       sense-ish in this context, though is awkwardly hard to parse visually
>       - ? (question mark) as a sigil here is prominent visually but 
>       overloaded conceptually by other conditional/optional constructs in 
> other 
>       languages
>    
> There was other feedback in this thread, less about a "tagged variable 
> capture" construct and more about how folk who really want some sort of 
> field punning want it to be either barewords (JS)-style syntax or keywords 
> (ruby)-style syntax; this new proposal still does not address that.
>
> *Problems remaining from previous proposal*
>
>    - Many still probably just want barewords or keywords style syntax for 
>    field punning
>    - Overloading an existing operator adds semantic overhead to the 
>    operator, and thereby the language
>
> *New proposal: Overloading the Module Attribute Operator*
>
> I was learning another language that uses @field as syntactic sugar, and 
> it occurred to me I'd passed over @ as a candidate for this syntax. I 
> realized that the module attribute operator is
>
>    - An operator already associated with compile-time expansion
>    - An operator already associated with assigning/reading values
>    - An operator with the correct associativity for this task
>    - An operator that currently raises at compile-time if used in this way
>       - So we can know the syntax for this new usage of it is not present 
>       in any existing Elixir programs
>    
> Again, my goal with this proposal is to support field punning in a way 
> that may not mirror other languages, but works with Elixir idioms and
>
>    - Can support both strings and atoms as keys
>    - Makes key typing syntactically explicit
>    - Can work in list, tuple, and map container literals
>    - Allows mixing strings/atoms inside containers
>    - Can work in pattern matches and guards
>    - Works with the pin operator
>    - Usage in map literals cannot be easily confused with tuple literals
>    - Works seamlessly with existing tooling, typechecking, etc
>
> Another perk is that it's trivial to implement support for it, and the pin 
> operator, in just 4 significant lines of code 
> <https://github.com/christhekeele/elixir/commit/24c472a96e681fb6ef9686d05861bde052c83ce3>
> .
>
> I want to re-open this discussion around a tagged-tuple variable capture 
> feature using it, as I really like how this reads (compared to past 
> proposals):  @:atom / @"foo" . I still feel like this implementation has 
> potential and that the full feature list above is worth the tradeoff of 
> overloading an existing operator.
>
> Nothing from the original proposal has otherwise changed, just the 
> operator in question. But, to reprise my examples from earlier so newcomers 
> to the thread can get a feel for what I mean by "tagged-tuple variable 
> capture":
> *Examples*
>
> 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.
>
> *Tagged-Tuple Variable Capture Literals* are what I'm calling an operator 
> that accepts a string or an atom literal, and expands to a two-tuple with 
> the literal as the first element, and inserts a reference to the variable 
> named by the literal as the second element. When this happens at the right 
> point during expansion, it naturally allows for field punning inside maps 
> and other constructs, a commonly requested Elixir feature. Today I'm 
> proposing overloading the module attribute operator @ with this new 
> functionality when given a string literal, atom literal, or a pin in front 
> of either:
>
> *Tagged Tuple Variable Capture Literals*
>
> @:foo
> # == *{:foo, foo}*
> # => {:foo, 1}
> @"foo"
> # == *{"foo", foo}*
> # => {"foo", 1}
>
> 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
>
> *Tagged Variable Literals in **Lists*
>
> Since tagged variable 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}
>
> *Tagged Variable Literals in **Maps*
>
> This expression is allowed 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}
>
>
>
> *Destructuring params in Phoenix*
> def handle_in(event, %{@"chat", @"question_id", @"data", @"attachment", 
> ...}, socket) do
> something_with(chat, question_id)
> ...
> end
>
> *Pin Operator Compatibility*
>
> foo = 1
>
> @^:foo = {:foo, 1}
> *# == {:foo, ^foo} = {:foo, 1}*
> # => {:foo, 1}
>
> @^:foo = {:foo, 2}
> *# == {:foo, ^foo} = {:foo, 2}*
> *# !> ** (MatchError) no match of right hand side value: {:foo, 2}*
>
> On Friday, June 30, 2023 at 1:01:45 PM UTC-5 Christopher Keele wrote:
>
>> Here's a summary of feedback on this proposal so far. 
>>
>> *Concerning the actual proposal itself* (ie, &:foo # => {:foo, foo}): 2 
>> against re-using the capture operator.
>>
>> *Concerning the followup of a dedicated operator* (ie, $:foo # => {:foo, 
>> foo}): roughly 2 for and 1 against (because it was not barewords), no 
>> major notes.
>>
>>    - Can support strings, atoms
>>    - Allows mixing strings/atoms
>>    - Key typing explicit
>>    - Works awkward with the pin operator
>>    - Does not look like tuples
>>    - Adds new syntax outside of collection constructs, for working with 
>>    tagged tuples for good/evil
>>
>> Overall, doesn't seem wildly popular, but I do think it does handle most 
>> of the problems of previous discussions well.
>>
>> It does not cleanly map to anything in other languages, but I view that 
>> as a pro because we want something more expressive than what they provide. 
>> I feel like that is part of the lack of enthusiasm, though: people want 
>> whatever feels most intuitive to them from other languages they've worked 
>> with, and as little new syntax to the language as possible. That's a hard 
>> needle to thread.
>>
>>
>> *Discussion on this thread about other proposals:*
>>
>>    - *"Barewords"* (ES6-style): %{foo, bar} People still want this, it 
>>    is still tricky for the usual reasons, and I don't *think* much new 
>>    has been contributed to the discussion in this thread.
>>       - Can only support strings or atoms
>>       - No mixing strings/atoms
>>       - Key typing hidden
>>       - Confusable with tuples
>>       - A new thing I've realized in my prototype is that it does work 
>>       better with the pin operator than others, something I haven't seen 
>>       discussed.
>>    - "Unmached pair" (*Ruby style*): %{foo:, bar:} This has always been 
>>    on the table but I do see more support for it than in past discussions. 
>>    Also, personally, my least favorite.
>>    - Lacks string support
>>       - No mixing strings/atoms
>>       - Key typing visually clear
>>       - Does not look like tuples
>>       - Pin operator problems
>>    - "*Pair literals*": %{:foo, "bar"} Some people seem to find this too 
>>    magical, some prefer this over my proposed dedicated operator that would 
>>    make it more explicit.
>>       - Can support strings, atoms
>>       - Allows mixing strings/atoms
>>       - Typing visually clear
>>       - Does not look like tuples
>>       - Pin operator problems
>>    
>>
>> *New proposals:*
>>
>>    - Adding support *at the tooling level*: 2 against (adding my voice 
>>    here). As was well-said, why would I write something that's not the 
>>    language's syntax?
>>    - Austin had an idea with using my operator *immediately alongside* 
>>    the map % literal to apply it to the whole expression. There are 
>>    problems with that, but
>>    - This in turn gave me an idea for a slightly new syntax we could use 
>>    to support barewords better. I guess call them *qualified* *barewords* 
>>    or something for now:
>>    - %{foo, bar}: barewords string keys
>>       - %:{foo, bar}: barewords atom keys
>>       - %Struct{foo, bar}: barewords struct keys
>>       - Initial thoughts:
>>          - Can support strings and atoms
>>          - No mixing strings/atoms
>>          - Key typing explicit, visually clear but hidden for strings
>>          - Sometimes looks like tuples for strings
>>          - Plays nice with pin operator
>>          - Still requires exploration with existing tokenization rules, 
>>          mixing rules, map update syntax
>>       
>>
>> I'd love to see someone champion any one of these syntaxes, put up some 
>> prototypes, get into the details of syntax ambiguities, and see dedicated 
>> discussions of pros/cons of explicit proposals in individual threads!
>>
>> I may continue investigating a prototype for pair literals (%{:foo, "bar"
>> }) or qualified barewords (%:{foo, bar}) as I get bolder with the erl 
>> source code.
>>
>> I welcome further discussion about the tagged-variable literal operator (
>> $:foo # => {:foo, foo}) in this thread, but probably won't contribute 
>> too much more to discussion on other proposals, as I want to focus on 
>> concrete proposals and tinker with prototypes for the more promising 
>> follow-ups! Thanks for the discussion, all!
>> On Thursday, June 29, 2023 at 9:50:39 AM UTC-5 torres....@gmail.com 
>> wrote:
>>
>>> I was catching up with the other discussions about this, and I'm having 
>>> seconds thoughts about Elixir having this syntax sugar for maps.
>>> It all sums up to what Austin pointed out here 
>>> <https://groups.google.com/g/elixir-lang-core/c/P6VprVlRd6k/m/hMmgSniMAgAJ>
>>> .
>>>
>>> The bottom line, IMO (and agreeing with Austin), would be to end having 
>>> something like `%{"foo", 'bar', :baz} = %{"foo" => 1, 'bar' => 2, :baz => 
>>> 3}; {foo, bar, baz} #=> {1, 2, 3}`, to solve the atom/string key problem, 
>>> and given that I'd prefer not to have it and leave Elixir as it is today.
>>> I also don't see any other tool (like a formatter or a language server) 
>>> doing this kind of job. Why would I write something if that's not the 
>>> language's syntax? If we open this precedent I'm not sure where we may end 
>>> up with (probably would end up like JS: being transcripted by transpilers).
>>> Plus, the misspelling argument is not appealing enough too, since a good 
>>> test covered application (which IMO should be our desire and intention) 
>>> would have that caught.
>>> To add a little more, since we should use atoms with caution due to 
>>> platform's limitations, we shouldn't make easier to enforce their usage, 
>>> and since the %{ foo } = %{ foo: "any value" } syntax sugar would favour 
>>> it, I am convinced enough *to not have* this on Elixir, contradicting 
>>> myself in a previous proposal.
>>>
>>> Em quinta-feira, 29 de junho de 2023 às 16:24:06 UTC+2, José Valim 
>>> escreveu:
>>>
>>>> : is not an operator at the user level for JS but it behaves like one 
>>>> syntactically. You can add or remove spaces on either side and it works. 
>>>> That’s not true for Ruby or Elixir as moving the spaces around is either 
>>>> invalid or has a different meaning.
>>>>
>>>

-- 
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 visit 
https://groups.google.com/d/msgid/elixir-lang-core/453ed8c1-4a8c-4db2-b651-e53591e0270dn%40googlegroups.com.

Reply via email to