Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

add => lambda syntax

  • Loading branch information...
commit 675898721c04d0bf155a85abf986eae99c37c0dc 1 parent 8fa99fc
Walter Bright authored
5  src/lexer.c
@@ -1146,6 +1146,10 @@ void Lexer::scan(Token *t)
1146 1146
                     else
1147 1147
                         t->value = TOKequal;            // ==
1148 1148
                 }
  1149
+                else if (*p == '>')
  1150
+                {   p++;
  1151
+                    t->value = TOKgoesto;               // =>
  1152
+                }
1149 1153
                 else
1150 1154
                     t->value = TOKassign;               // =
1151 1155
                 return;
@@ -3149,6 +3153,7 @@ void Lexer::initKeywords()
3149 3153
     Token::tochars[TOKat]               = "@";
3150 3154
     Token::tochars[TOKpow]              = "^^";
3151 3155
     Token::tochars[TOKpowass]           = "^^=";
  3156
+    Token::tochars[TOKgoesto]           = "=>";
3152 3157
 #endif
3153 3158
 
3154 3159
      // For debugging
3  src/lexer.h
@@ -34,7 +34,7 @@ struct Module;
34 34
         =       !       ~       @
35 35
         ^^      ^^=
36 36
         ++      --
37  
-        .       ->      :       ,
  37
+        .       ->      :       ,       =>
38 38
         ?       &&      ||
39 39
  */
40 40
 
@@ -168,6 +168,7 @@ enum TOK
168 168
         TOKat,
169 169
         TOKpow,
170 170
         TOKpowass,
  171
+        TOKgoesto,
171 172
 #endif
172 173
 
173 174
         TOKMAX
36  src/parse.c
@@ -5105,6 +5105,21 @@ Expression *Parser::parsePrimaryExp()
5105 5105
                     tempinst->tiargs = parseTemplateArgument();
5106 5106
                 e = new ScopeExp(loc, tempinst);
5107 5107
             }
  5108
+            else if (token.value == TOKgoesto)
  5109
+            {   // identifier => expression
  5110
+                Type *at = new TypeIdentifier(loc, id);
  5111
+                Parameter *a = new Parameter(0, at, NULL, NULL);
  5112
+                Parameters *arguments = new Parameters();
  5113
+                arguments->push(a);
  5114
+                TypeFunction *tf = new TypeFunction(arguments, NULL, 0, linkage, 0);
  5115
+                FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, 0, tf, TOKdelegate, NULL);
  5116
+                check(TOKgoesto);
  5117
+                Expression *ae = parseAssignExp();
  5118
+                fd->fbody = new ReturnStatement(loc, ae);
  5119
+                fd->endloc = loc;
  5120
+                e = new FuncExp(loc, fd);
  5121
+                break;
  5122
+            }
5108 5123
             else
5109 5124
                 e = new IdentifierExp(loc, id);
5110 5125
             break;
@@ -5430,7 +5445,22 @@ Expression *Parser::parsePrimaryExp()
5430 5445
         }
5431 5446
 
5432 5447
         case TOKlparen:
5433  
-            if (peekPastParen(&token)->value == TOKlcurly)
  5448
+        {   enum TOK past = peekPastParen(&token)->value;
  5449
+
  5450
+            if (past == TOKgoesto)
  5451
+            {   // (arguments) => expression
  5452
+                int varargs;
  5453
+                Parameters *arguments = parseParameters(&varargs);
  5454
+                TypeFunction *tf = new TypeFunction(arguments, NULL, varargs, linkage, 0);
  5455
+                FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, 0, tf, TOKdelegate, NULL);
  5456
+                check(TOKgoesto);
  5457
+                Expression *ae = parseAssignExp();
  5458
+                fd->fbody = new ReturnStatement(loc, ae);
  5459
+                fd->endloc = loc;
  5460
+                e = new FuncExp(loc, fd);
  5461
+                break;
  5462
+            }
  5463
+            else if (past == TOKlcurly)
5434 5464
             {   // (arguments) { statements... }
5435 5465
                 save = TOKdelegate;
5436 5466
                 goto case_delegate;
@@ -5441,6 +5471,7 @@ Expression *Parser::parsePrimaryExp()
5441 5471
             e->parens = 1;
5442 5472
             check(loc, TOKrparen);
5443 5473
             break;
  5474
+        }
5444 5475
 
5445 5476
         case TOKlbracket:
5446 5477
         {   /* Parse array literals and associative array literals:
@@ -5498,7 +5529,6 @@ Expression *Parser::parsePrimaryExp()
5498 5529
              */
5499 5530
             Parameters *arguments;
5500 5531
             int varargs;
5501  
-            FuncLiteralDeclaration *fd;
5502 5532
             Type *t;
5503 5533
             StorageClass stc = 0;
5504 5534
 
@@ -5525,7 +5555,7 @@ Expression *Parser::parsePrimaryExp()
5525 5555
 
5526 5556
             TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage, stc);
5527 5557
 
5528  
-            fd = new FuncLiteralDeclaration(loc, 0, tf, save, NULL);
  5558
+            FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, 0, tf, save, NULL);
5529 5559
             parseContracts(fd);
5530 5560
             e = new FuncExp(loc, fd);
5531 5561
             break;
17  test/runnable/template4.d
@@ -888,7 +888,7 @@ void test33() {
888 888
     Composer!(double) comp;
889 889
     comp += delegate double (double x) { return x/3.0;};
890 890
     comp += delegate double (double x) { return x*x;};
891  
-    comp += delegate double (double x) { return x+1.0;};
  891
+    comp += (double x) => x + 1.0;
892 892
     writefln("%f", comp(2.0));
893 893
 
894 894
     // Try function objects
@@ -1022,6 +1022,20 @@ void instantiate4652()
1022 1022
 
1023 1023
 /*********************************************************/
1024 1024
 
  1025
+int bar39(alias dg)(int i)
  1026
+{
  1027
+    return dg(i);
  1028
+}
  1029
+
  1030
+void test39()
  1031
+{
  1032
+    auto i = bar39!(a => a + 1)(3);
  1033
+    if (i != 4)
  1034
+	assert(0);
  1035
+}
  1036
+
  1037
+/*********************************************************/
  1038
+
1025 1039
 int main()
1026 1040
 {
1027 1041
     test1();
@@ -1062,6 +1076,7 @@ int main()
1062 1076
     test36();
1063 1077
     test37();
1064 1078
     test38();
  1079
+    test39();
1065 1080
 
1066 1081
     printf("Success\n");
1067 1082
     return 0;

20 notes on commit 6758987

Jarrett Billingsley

Only took four years!.. no wait, five

But in seriousness, at least this means an end to the silly string mixins.

JakobOvrum

Finally! Thanks a lot!

Denis Shelomovskij

The one I was missing after C# for these years. Thanks!

simendsjo

Same here. The other feature I really miss is object initializers: http://msdn.microsoft.com/en-us/library/bb384062.aspx and attributes.

class C {
int a, b;
}

D:
auto c = new C();
with(c) {
a = 1;
b = 2;
}

C#:
var c = new C() { a = 1, b = 2 };

Walter Bright
simendsjo

That works for structs, but for class I get "Error: no constructor for C".
Even if it did work, rearranging the fields, or adding new ones, would give unexpected results. It's a feature I don't dare to use.. Adding a constructor that has the same number and types of parameters, but that doesn't assign in the same order would also compile without a warning, but could change the code completely.

auto c = new C(); with(c) {
a = 1;
b = 2;
}

Is a safer trick, although a bit longer to write.

Dejan Lekic

A big step forward! :)

Witold Baryluk

Hmm, i have mixed feeling about this. Syntax isn't very Dish.

Also how is able compile know what this means:

void main() {
  int a = 5;
  auto x =  a => a/2;
}

is x an bool x = true, or is x a delegate?

a -> a/2;

would be much better syntax (somehow borowed from Erlang).

Any way I do not really think it is needed, because simple lambdas already can be expressed very shortly:

I also doesn't understand how actually it work:

a => a+1;

is equivalent to

delegate (a) { return a+1; };

But this is illegal construction, because there is no type of a. (And I checked grammar it is needed, before 'Declarator' rule).

It may possibly mean, delegate (T)(a) { return a+1; }, but there is no such things as delegate templates in D.

Jarrett Billingsley

=> isn't a comparison operator, so your first example is unambiguous.

As for how it works: the types are inferred, of course, but that would be too useful for general use to make it general. Much like how the 'auto' return type mysteriously only works on templated functions.

Don Clugston
Collaborator

Much like how the 'auto' return type mysteriously only works on templated functions.
What do you mean? This compiles:
auto blah() { return 3; }

Witold Baryluk

This have nothing to do with auto return type. It is very simple how it works. It just uses type of expression passed to return(s), or void. Nothing mysterious here.

delegate (double a) { return a+1; }

is actually

delegate auto(double a) { return a+1; }

My question was that

delegate (a) { return a+1; }
delegate auto(a) { return a+1; }

looks to be polymorphic function/delegate, just like

delegate (T)(T a) { return a+1; }

But it is incorrect also (there is no such things like delegate templates.

So I do not really understand how a=>a+1; or delegate (a) { return a+1; } is correct in example with bar39!(a => a + 1)(3);

Similary what with other examples without argument types or multiple arguments?

(a) => (a+1); // is this correct?
(double a, double b) => (a>b); // should be correct, right?
(a,b) => (a>b);  // is this correct ?

As of =>, you are correct, it is distinct from >=. But still -> will make me fell me and my brain much better (it is more obvious, it is different from =>). => have oviously some advantages, like being similar to mathemtical inference symbol, but -> on the other hand is more similar to function mapping symbol. Which is exactly what we want here.

Witold Baryluk

One more non obvious example:

x => y >= x;
x => y => x;

I think,

x -> y >= x;
x -> y -> x;
x -> y -> x+y;

is MUCH clearer. Not only this syntax is used in Erlang, but also in Haskell. => is more from C# I think, or Scala. Was there any discussion about which to choose? I do not follow much newsgroup recently so have no idea.

Denis Shelomovskij

=> is a C# lambda operator so it's common for many people.

Type of expression like a => a is inferred from using.

auto func = a => a; // error
int function(int) d = a=>a; // compiles

If it is passed as alias template parameter, the expression is passed (without instantiation). One can call t!(a=>a)(); or t!((a){ return a; })(); where t is:

void t(alias f)() {
    pragma(msg, typeof( f(1)   )); // prints int,    like (a=>a)(1)
    pragma(msg, typeof( f(1.0) )); // prints double, like (a=>a)(1.0)
}
Witold Baryluk

Interesting, still -> is cleaner, and much more intuitive, especially when mixing with >=. It is also used in Haskell, Erlang, OCaml, C++11, and classic lambda calculus notation. I also think that => is bad choice, not only because it is less clear than ->, but now it could be possible (in theory) that one will write => when one should >= and program will compile anyway without error message.

Maybe we cannot use ->, because it is dereferencing operator in C/C++? But AFAIK there is no dereferencing operator in D. There is no such token as -> in lexer.

So compiler now perform lazy typing in some sense, because before that D was only doing forward type inference. Now it is more like full type inference known from Haskell or OCaml (aka Hindley–Milner algorithm).

Anyway D grammar documentation still needs updating, because it says:

Lambda:
  Identifier => AssignExpression
  ParameterAttributes => AssignExpression

and that first form is equivalent to

delegate ( Identifier ) { return AssignExpression; }

but, looking at delegate rules:

FunctionLiteral:
    function Type_opt ParameterAttributes_opt FunctionBody
    delegate Type_opt ParameterAttributes opt FunctionBody
    ParameterAttributes FunctionBody
    FunctionBody

ParameterAttributes:
    Parameters
    Parameters FunctionAttributes

Parameters:
    ( ParameterList )
    ( )

ParameterList:
    Parameter
    Parameter , ParameterList
    ...

Parameter:
    InOut_opt BasicType Declarator
    InOut_opt BasicType Declarator ...
    InOut_opt BasicType Declarator = DefaultInitializerExpression
    InOut_opt Type
    InOut_opt Type ...

So it looks that it is impossible to derive expression:

delegate (x) { return x; }

because there must be a BasicType before 'x'.

Denis Shelomovskij

If something compiles in both C and D it should behave the same way if there is no strong reason against. With -> as lambda operator e.g, f(x->y); can choose incorrect overload.

Witold Baryluk

There is no function overloads in C. You probably was thinking about C++. If anybody will try to source port C++ source (very bad idea) and will forgot to change -> (dereferencing) to dot (.), (which can be done automatically very easly anyway!) then it is very unlikly there will be anything which will match delegate (->), because there is no such constructs in C++. Somehow artificial example IMHO.

Also it will need even more type inferance to compile automatically, like:

auto f(delegate int(int) dg) {
   return dg(3);
} 
f(x => x+2); //     f(x->x+2);     ,  like (x->x)+2 vs x->(x+2);, second one being inccorrect in C++

will this even work? I guess maybe, because you given similar example:

delegate int(int) dg = x => 13*x;
delegate int(int) dg = (x => 13*x);

@baryluk: I don't find it any cleaner or more intuitive. This is just pointless bikeshedding.

Denis Shelomovskij

I knew you will say "There is no function overloads in C", but with an exclamation mark or two. I mean, that and overload accepting a function is in D code only (it can be a result of renaming f_func, e.g.).

will this even work?

Yes, if you will change delegate int(int) to int delegate(int).

Witold Baryluk

@Poita: If improving language (to have less potential errors), discussion about incorrect current grammar is pointless then I do not know why we have discussions about language new features and improvements anyway. If you think -> is not cleaner or intuitive, then it is only you opinion (maybe you have been using C#, or something; I guess some people can like one or another). Was asking if there was some discussion or poll about choising => (I guess borrowed from C#, but still how much people come to D from C#, compared to other languages), and I cannot find anything.

Witold Baryluk

@denis-sh: Thanks. Will think more about it. It indeed may be good reason not to use ->.

BTW, slightl offtopic: Still do not understand why D have delegate int(int x) { ... } construct when doing expression, but int delegate(int) as it's type.

Still grammar needs update. ;)

Please sign in to comment.
Something went wrong with that request. Please try again.