I've realized that this syntax is now sufficiently viable outside of the 
parser to be implemented as a pure Elixir macro library, without need for a 
fork of the language, so will be releasing it and circulating on the forums 
for more broader feedback. It's hard to sell even myself on such novel 
syntax, so I figure userland is a better space for it to be tested out 
before re-mounting a language proposal.

On Friday, December 20, 2024 at 5:40:45 PM UTC-6 Christopher Keele wrote:

> 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/e93a8414-46e6-4574-90d6-0b6b01ed7ea7n%40googlegroups.com.

Reply via email to