Thread overview
The Tail Operator '#' enables clean typechecked builders with minimal language changes.
Jan 25, 2019
FeepingCreature
Jan 25, 2019
Eugene Wissner
Jan 25, 2019
Kagamin
Jan 25, 2019
bauss
Jan 25, 2019
FeepingCreature
January 25, 2019
A problem that's come up before is how to specify builders in a typechecked manner. In my language syntax experiment, Neat, I'd copied Haskell's '$' operator, which means "capture everything to the right of here in one set of parens", in order to avoid writing parentheses expressions:

    foo(2 + 2);
    // becomes
    foo $ 2 + 2;

But what if we wanted the *opposite*? What if we wanted to capture everything to the *left* of an operator in one set of parens? Enter the tail operator:

    (2 + 2).foo;
    // becomes
    2 + 2 #.foo;

Why is this useful?

Well, we've had the debate before about struct initializers. The irksome thing is that the language is *almost* there! We can already write function calls as assignments:

    foo(5);
    // becomes
    foo = 5;

The main problem is that there's no way in the language to chain assignments together in that syntax. If we could, we could generate very clean looking builder expressions. But

    (((Foo.build
      .a = 3)
      .b = 4)
      .c = 5)
      .value;

Just doesn't look good and isn't as easy to modify as it should be either.

But with the tail operator, this becomes:

    Foo.build
      #.a = 3
      #.b = 4
      #.c = 5
      #.value;

Which is pure, typechecked, easy to extend and still readable.

What do you think?
January 25, 2019
On Friday, 25 January 2019 at 07:24:10 UTC, FeepingCreature wrote:
> A problem that's come up before is how to specify builders in a typechecked manner. In my language syntax experiment, Neat, I'd copied Haskell's '$' operator, which means "capture everything to the right of here in one set of parens", in order to avoid writing parentheses expressions:
>
>     foo(2 + 2);
>     // becomes
>     foo $ 2 + 2;
>
> But what if we wanted the *opposite*? What if we wanted to capture everything to the *left* of an operator in one set of parens? Enter the tail operator:
>
>     (2 + 2).foo;
>     // becomes
>     2 + 2 #.foo;
>
> Why is this useful?
>
> Well, we've had the debate before about struct initializers. The irksome thing is that the language is *almost* there! We can already write function calls as assignments:
>
>     foo(5);
>     // becomes
>     foo = 5;
>
> The main problem is that there's no way in the language to chain assignments together in that syntax. If we could, we could generate very clean looking builder expressions. But
>
>     (((Foo.build
>       .a = 3)
>       .b = 4)
>       .c = 5)
>       .value;
>
> Just doesn't look good and isn't as easy to modify as it should be either.
>
> But with the tail operator, this becomes:
>
>     Foo.build
>       #.a = 3
>       #.b = 4
>       #.c = 5
>       #.value;
>
> Which is pure, typechecked, easy to extend and still readable.
>
> What do you think?

$ is a simple function in Haskell that is defined in a library, the same as '+', '-' are just functions and not special operators as in D. In D you have to define each operator in the language itself and the compiler should understand them. Defining a language construct for each use case doesn't seems pretty to me, it makes the grammar overcomplicated.
January 25, 2019
On Friday, 25 January 2019 at 07:24:10 UTC, FeepingCreature wrote:
> But with the tail operator, this becomes:
>
>     Foo.build
>       #.a = 3
>       #.b = 4
>       #.c = 5
>       #.value;
>
> Which is pure, typechecked, easy to extend and still readable.
>
> What do you think?

This is shorter and cleaner:

    Foo.build
      .a(3)
      .b(4)
      .c(5)
      .value;
January 25, 2019
On Friday, 25 January 2019 at 08:33:51 UTC, Kagamin wrote:
> On Friday, 25 January 2019 at 07:24:10 UTC, FeepingCreature wrote:
>> But with the tail operator, this becomes:
>>
>>     Foo.build
>>       #.a = 3
>>       #.b = 4
>>       #.c = 5
>>       #.value;
>>
>> Which is pure, typechecked, easy to extend and still readable.
>>
>> What do you think?
>
> This is shorter and cleaner:
>
>     Foo.build
>       .a(3)
>       .b(4)
>       .c(5)
>       .value;

And easy to implement:

https://run.dlang.io/is/0Z0GKb
January 25, 2019
On Friday, 25 January 2019 at 10:31:15 UTC, bauss wrote:
>> This is shorter and cleaner:
>>
>>     Foo.build
>>       .a(3)
>>       .b(4)
>>       .c(5)
>>       .value;
>
> And easy to implement:
>
> https://run.dlang.io/is/0Z0GKb

Yeah but it doesn't look like assignment. And it's only as readable because the examples are oversimplified.

    Foo.build
      .property(source.property.call(bla, bla, bla).map!"a.BlaAttribute"))

And it becomes much harder to understand the builder principle as compared to

    Foo.build
      #.property = source.property.call(bla, bla, bla).map!"a.BlaAttribute"

We have operators for a reason.