www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The Tail Operator '#' enables clean typechecked builders with minimal

reply FeepingCreature <feepingcreature gmail.com> writes:
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?
Jan 24
next sibling parent Eugene Wissner <belka caraus.de> writes:
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.
Jan 25
prev sibling parent reply Kagamin <spam here.lot> writes:
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;
Jan 25
parent reply bauss <jj_1337 live.dk> writes:
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
Jan 25
parent FeepingCreature <feepingcreature gmail.com> writes:
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.
Jan 25