-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathp0573r2.html
More file actions
executable file
·960 lines (805 loc) · 55.1 KB
/
p0573r2.html
File metadata and controls
executable file
·960 lines (805 loc) · 55.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
<html>
<head>
<title>Abbreviated Lambdas for Fun and Profit</title>
<style type="text/css">html {
position: relative;
max-width: 1024px;
height: 100%;
}
body {
font-family: Helvetica, arial, sans-serif;
font-size: 14px;
line-height: 1.6;
padding-top: 10px;
padding-bottom: 10px;
background-color: white;
padding: 30px;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
a {
color: #4183C4;
}
a.absent {
color: #cc0000;
}
a.anchor {
display: block;
padding-left: 30px;
margin-left: -30px;
cursor: pointer;
position: absolute;
top: 0;
left: 0;
bottom: 0;
}
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
cursor: text;
position: relative;
}
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==) no-repeat 10px center;
text-decoration: none;
}
h1 tt, h1 code {
font-size: inherit;
}
h2 tt, h2 code {
font-size: inherit;
}
h3 tt, h3 code {
font-size: inherit;
}
h4 tt, h4 code {
font-size: inherit;
}
h5 tt, h5 code {
font-size: inherit;
}
h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: black;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #cccccc;
color: black;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777777;
font-size: 14px;
}
p, blockquote, ol, dl, li, table, pre {
margin: 15px 0;
}
hr {
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;
border: 0 none;
color: #cccccc;
height: 4px;
padding: 0;
}
body>h2:first-child {
margin-top: 0;
padding-top: 0;
}
body>h1:first-child {
margin-top: 0;
padding-top: 0;
}
body>h1:first-child+h2 {
margin-top: 0;
padding-top: 0;
}
body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
margin-top: 0;
}
li p.first {
display: inline-block;
}
li {
margin: 0;
}
ol {
padding-left: 30px;
}
ul :first-child, ol :first-child {
margin-top: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt> :first-child {
margin-top: 0;
}
dl dt> :last-child {
margin-bottom: 0;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd> :first-child {
margin-top: 0;
}
dl dd> :last-child {
margin-bottom: 0;
}
blockquote {
border-left: 4px solid #dddddd;
padding: 0 15px;
color: #777777;
}
blockquote> :first-child {
margin-top: 0;
}
blockquote> :last-child {
margin-bottom: 0;
}
table {
padding: 0;
border-collapse: collapse;
}
table tr {
border-top: 1px solid #cccccc;
background-color: white;
margin: 0;
padding: 0;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
table tr th {
font-weight: bold;
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px;
}
table tr td {
border: 1px solid #cccccc;
margin: 0;
padding: 6px 13px;
}
table tr th :first-child, table tr td :first-child {
margin-top: 0;
}
table tr th :last-child, table tr td :last-child {
margin-bottom: 0;
}
td {
vertical-align: top;
}
img {
max-width: 100%;
}
span.frame {
display: block;
overflow: hidden;
}
span.frame>span {
border: 1px solid #dddddd;
display: block;
float: left;
overflow: hidden;
margin: 13px 0 0;
padding: 7px;
width: auto;
}
span.frame span img {
display: block;
float: left;
}
span.frame span span {
clear: both;
color: #333333;
display: block;
padding: 5px 0 0;
}
span.align-center {
display: block;
overflow: hidden;
clear: both;
}
span.align-center>span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: center;
}
span.align-center span img {
margin: 0 auto;
text-align: center;
}
span.align-right {
display: block;
overflow: hidden;
clear: both;
}
span.align-right>span {
display: block;
overflow: hidden;
margin: 13px 0 0;
text-align: right;
}
span.align-right span img {
margin: 0;
text-align: right;
}
span.float-left {
display: block;
margin-right: 13px;
overflow: hidden;
float: left;
}
span.float-left span {
margin: 13px 0 0;
}
span.float-right {
display: block;
margin-left: 13px;
overflow: hidden;
float: right;
}
span.float-right>span {
display: block;
overflow: hidden;
margin: 13px auto 0;
text-align: right;
}
code, tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
.highlight pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre {
background-color: #f8f8f8;
border: 1px solid #cccccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
sup {
font-size: 0.83em;
vertical-align: super;
line-height: 0;
}
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: #555;
vertical-align: middle;
background-color: #fcfcfc;
border: solid 1px #ccc;
border-bottom-color: #bbb;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #bbb
}
* {
-webkit-print-color-adjust: exact;
}
ins {
color: #00A000
}
del {
color: #A00000
}
</style><style type="text/css">
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"], pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 11px;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection, code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"], pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"], pre[class*="language-"] {
background: #f8f8f8;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment, .token.prolog, .token.doctype, .token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted {
color: #905;
}
.token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted {
color: #690;
}
.token.operator {
color: #a67f59;
}
.token.entity, .token.url, .language-css .token.string, .style .token.string {
color: #a67f59;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule, .token.attr-value, .token.keyword {
color: #07a;
}
.token.function {
color: #DD4A68;
}
.token.regex, .token.important, .token.variable {
color: #e90;
}
.token.important, .token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
</style>
<script type="text/javascript">
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function(e){var t=n.util.type(e);switch(t){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=n.util.clone(e[r]));return a;case"Array":return e.map&&e.map(function(e){return n.util.clone(e)})}return e}},languages:{extend:function(e,t){var a=n.util.clone(n.languages[e]);for(var r in t)a[r]=t[r];return a},insertBefore:function(e,t,a,r){r=r||n.languages;var l=r[e];if(2==arguments.length){a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(l[i]=a[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==t)for(var i in a)a.hasOwnProperty(i)&&(o[i]=a[i]);o[s]=l[s]}return n.languages.DFS(n.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=o)}),r[e]=o},DFS:function(e,t,a,r){r=r||{};for(var l in e)e.hasOwnProperty(l)&&(t.call(e,l,e[l],a||l),"Object"!==n.util.type(e[l])||r[n.util.objId(e[l])]?"Array"!==n.util.type(e[l])||r[n.util.objId(e[l])]||(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,l,r)):(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,null,r)))}},plugins:{},highlightAll:function(e,t){var a={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};n.hooks.run("before-highlightall",a);for(var r,l=a.elements||document.querySelectorAll(a.selector),i=0;r=l[i++];)n.highlightElement(r,e===!0,a.callback)},highlightElement:function(t,a,r){for(var l,i,o=t;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1],i=n.languages[l]),t.className=t.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=t.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var s=t.textContent,u={element:t,language:l,grammar:i,code:s};if(!s||!i)return n.hooks.run("complete",u),void 0;if(n.hooks.run("before-highlight",u),a&&_self.Worker){var c=new Worker(n.filename);c.onmessage=function(e){u.highlightedCode=e.data,n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},c.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=n.highlight(u.code,u.grammar,u.language),n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(t),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},highlight:function(e,t,r){var l=n.tokenize(e,t);return a.stringify(n.util.encode(l),r)},tokenize:function(e,t){var a=n.Token,r=[e],l=t.rest;if(l){for(var i in l)t[i]=l[i];delete t.rest}e:for(var i in t)if(t.hasOwnProperty(i)&&t[i]){var o=t[i];o="Array"===n.util.type(o)?o:[o];for(var s=0;s<o.length;++s){var u=o[s],c=u.inside,g=!!u.lookbehind,h=!!u.greedy,f=0,d=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var m=r[p];if(r.length>e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var y=u.exec(m),v=1;if(!y&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p<r.length-2&&(k+=r[p+2].matchedStr||r[p+2]),u.lastIndex=0,y=u.exec(k),!y)continue;var w=y.index+(g?y[1].length:0);if(w>=m.length)continue;var _=y.index+y[0].length,P=m.length+b.length;if(v=3,P>=_){if(r[p+1].greedy)continue;v=2,k=k.slice(0,P)}m=k}if(y){g&&(f=y[1].length);var w=y.index+f,y=y[0].slice(f),_=w+y.length,S=m.slice(0,w),O=m.slice(_),j=[p,v];S&&j.push(S);var A=new a(i,c?n.tokenize(y,c):y,d,y,h);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+"</"+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
</script>
<script type="text/javascript">
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
</script>
<script type="text/javascript">
Prism.languages.c=Prism.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0},directive:{pattern:/(#\s*)\b(define|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/,lookbehind:!0,alias:"keyword"}}},constant:/\b(__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|stdin|stdout|stderr)\b/}),delete Prism.languages.c["class-name"],delete Prism.languages.c["boolean"];
</script>
<script type="text/javascript">
Prism.languages.cpp=Prism.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),Prism.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}});
</script>
</head>
<body>
<address align=right>
Document number: P0573R2 <br />
Date: 2017-10-08 <br />
Audience: Evolution Working Group <br />
Reply-To: Barry Revzin <barry.revzin@gmail.com> <br />
Tomasz Kamiński <tomaszkam@gmail.com>
</address>
<hr/>
<h1 align=center>Abbreviated Lambdas for Fun and Profit</h1>
<h2>Contents</h2>
<ol>
<li><a href="#Motivation">Motivation</a></li>
<li><a href="#Proposal">Proposal</a></li>
<li><a href="#Examples">Examples</a></li>
<li><a href="#Hyper">Hyper-abbreviated lambdas</a></li>
<li><a href="#Priors">Prior Work and Effects on Existing Code</a></li>
<li><a href="#Revisions">Revision History</a></li>
<li><a href="#Acks">Acknowledgements and References</a></li>
</ol>
<a name="Motivation" ></a><h2>1. Motivation</h2>
<p>The introduction of lambdas in C++11 transformed the landscape of C++ tremendously by allowing users to write arbitrary functions, predicates, and callbacks in-situ. Suddenly, instead of having to write function objects somewhere far from the code, we could write them inline. C++14 improved lambdas both by expanding their functionality (with generic lambdas) and by making them shorter (by being able to use <code class="language-cpp">auto</code> instead of spelling out the full type name of the parameters).</p>
<p>While lambdas are at the point where nearly arbitrary functionality is possible, it is still cumbersome and at times unnecessarily verbose to write out lambdas in the simplest of cases. A disproportionate number of lambda expressions are extremely short, consisting solely of a single expression in the body. These simple lambdas typically occur in one (or more) of these situations:
<ul>
<li> Unary/Binary predicates into algorithms.
<li> Callbacks
<li> Binding - simply reordering or adding new arguments for other functions. Basically, a better <code class="language-cpp">std::bind</code>.
<li> Lifting - taking overloaded functions and lifting them into a generic lambda so that they can be passed into a function.
</ul>
The main idea linking all of these situations is that we're trying to lift an <i>expression</i> into a function. That expression isn't merely an implementation detail: it's the most important detail. The goal as a programmer is to, as transparently as possible, make the expression we want to use available as a function in the context we want to use it. But in all these cases, the amount of code necessary to write this transparent lambda is excessive compared to the actual functionality being expressed.
<p> A perfect example of this excessive and complicated code necessary to express a seemingly simple idea is the lifting of overload sets [1]. We cannot simply write:
<pre><code class="language-cpp">template <class T>
T twice(T x) { return x + x; }
template <class I>
void f(I first, I last) {
transform(first, last, twice); // error
}
</code></pre>
<p>We need to wrap <code class="inline">twice</code> in a lambda. But the correct way to do that is quite complex. We need to keep lots of details in mind. First, we of course need a generic lambda:
<pre><code class="language-cpp">[](auto x) { return twice(x); }</code></pre>
But then, this incurs unnecessary copies in and out, so we would want to forward into and out of the lambda:
<pre><code class="language-cpp">[](auto&& x) { return twice(std::forward<decltype(x)>(x)); }</code></pre>
But now, if <code class="inline">twice</code> returned a reference type, the lambda drops that. In order to mirror that properly, we would need a trailing return type:
<pre><code class="language-cpp">[](auto&& x) -> decltype(auto) { return twice(std::forward<decltype(x)>(x)); }</code></pre>
The problems with this approach are more subtle, but no less real.
<h3>1.1. SFINAE and <code class="language-cpp">noexcept</code></h3>
The motivation is to <i>transparently</i> wrap an expression to make it usable as a function. If we have an overload set, like:
<pre><code class="language-cpp">int& foo(int& ) noexcept;
std::string foo(char const* );</code></pre>
We can use <code class="language-cpp">foo</code> in generic code directly by name and query whether it's invocable with a given argument type and query whether it's <code class="language-cpp">noexcept</code> with a given argument type. This is <i>prima facie</i> important and useful. If we naively wrap <code class="language-cpp">foo</code> in a lambda that simply returns:
<pre><code class="language-cpp">[](auto&& arg) -> decltype(auto) { return foo(arg); }</code></pre>
<p>Then we immediately lose both abilities. This lambda is <i>never</i> <code class="language-cpp">noexcept</code>. Since the user took the effort and deliberately marked <code class="language-cpp">foo(int& )</code> as being <code class="language-cpp">noexcept</code>, we lose that information if we just drop it on the floor. This lambda also advertises itself as being invocable with any argument type, which can lead to problems.
<p>With only a single expression, there is no advantage to writing <code class="language-cpp">decltype(auto)</code> over <code class="language-cpp">decltype(foo(arg))</code>. But in certain contexts, preferring the latter can be the difference between a program that compiles and does the expected thing - and a program that fails to compile.
<pre><code class="language-cpp">bool call_me_maybe(function<bool(int)> ); // #1
bool call_me_maybe(function<bool(string) > ); // #2
// non-SFINAE-friendly lambda
call_me_maybe([](auto x) { return x == 2; }); // error! lots and lots of diagnostic
// SFINAE-friendly lambda
call_me_maybe([](auto x) -> decltype(x == 2) { return x == 2; }); // OK, calls #1</code></pre>
The intent of the programmer is simple, and the reason for the compile failure of the first attempt is quite complex (attempting to instantiate <code class="language-cpp">function<bool(string)></code> involves instantiating the particular <code class="language-cpp">operator()</code> of the lambda to determine its return type, but this instantiation isn't considered in the immediate context and so this failure is a hard error rather than a substitution failure). The technique to fix this, the trailing <code class="language-cpp">decltype</code>, is an advanced technique - far more advanced than the problem seems to call for.
<h3>1.2. Does Concepts solve the problem?</h3>
<p> With Concepts, we have a better, clearer way to constrain templates. It's tempting to believe that the introduction of Concepts to C++2a would obviate us of this problem. But Concepts only helps us if we help Concepts.
<h4>1.2.1. Concept error messages</h4>
<p>Let's consider a constrained implementation of <code class="language-cpp">std::find_if</code>, where we only focus on the predicate constraint:
<pre><code class="language-cpp">template <class F, class... Args>
concept Predicate = requires(F f, Args... args) {
{ std::invoke(f, args...) } -> bool;
};
template <class Iter, Predicate<typename std::iterator_traits<Iter>::reference F>
Iter find_if(Iter, Iter, F );
</code></pre>
<p>Now, what happens when we try to invoke this incorrectly?
<pre><code class="language-cpp">std::vector<std::string> vs;
find_if(vs.begin(), vs.end(), [](auto const& s) { return s == 2; }); // 1: Concept unfriendly
find_if(vs.begin(), vs.end(), [](auto const& s) -> decltype(s == 2) { return s == 2; }); // 2: Concept friendly</code></pre>
<p>Both invocations are erroneous. However, the non-Concept-friendly lambda generates <a href="https://wandbox.org/permlink/puI6CQH4UVyQzOU0">240 lines of errors</a>, going through all the various, irrelevant overloads of <code class="language-cpp">operator==</code> that don't work.
<p>On the other hand, the second invocation, the Concept-friendly lambda, produces a short message clearly indicating that the lambda does not model the <code class="language-cpp">Predicate</code>. That error, in <a href="https://wandbox.org/permlink/diRMsYwPgHu2jYPp">its entirety</a>:
<pre style="padding-left: 30px">prog.cc:17:89: error: cannot call function 'Iterator find_if(Iterator, Iterator, F) [with Iterator = __gnu_cxx::__normal_iterator<std::__cxx11::basic_stringchar>*, std::vector<std::__cxx11::basic_string<char> > >; F = main()::<lambda(const auto:1&)>]'
find_if(vs.begin(), vs.end(), [](auto const& s) -> decltype(s == 2) { return s == 2; }); //2
^
prog.cc:11:10: note: constraints not satisfied
Iterator find_if(Iterator b, Iterator e, F f);
^~~~~~~
prog.cc:6:14: note: within 'template<class F, class ... T> concept const bool Predicate<F, T ...> [with F = main()::<lambda(const auto:1&)>; T = {std::__iterator_traits<__gnu_cxx::__normal_iterator<std::__cxx11::basic_string<char>*, std::vector<std::__cxx11::basic_string<char> > >, void>::reference}]'
concept bool Predicate = requires(F f, T... t) {
^~~~~~~~~
prog.cc:6:14: note: with 'main()::<lambda(const auto:1&)> f'
prog.cc:6:14: note: with 'std::__cxx11::basic_string<char>& t#0'
prog.cc:6:14: note: the required expression 'f(t ...)' would be ill-formed</pre>
<h4>1.2.2. Concept overloading</h4>
<p>The situation is no better when we try to overload with Concepts. Consider a seemingly innocuous example where we try to implement <code class="language-cpp">on_each_month()</code>:
<pre><code class="language-cpp">void on_each_month(Predicate<day_point> f);
void on_each_month(Predicate<std::string_view> f);</code></pre>
<p>The intent of this function is to use the provided predicate to filter some set of dates. The first overload provides a <code class="language-cpp">day_point</code> in the numeric format, the second is an optimized version that provides a serialized date. Let's try to use one:
<pre><code class="language-cpp">on_each_month([](auto d) { return day_of_week(d) == friday; });</code></pre>
<p>Even though we might perceive that one overload is viable, but the other not, this call will also fail to compile due to a hard error in attempting to instantiate the <code class="language-cpp">string_view</code> overload (and again, with a very long list of diagnostics). This problem is the same fundamental problem demonstrated in the previous two examples. To fix this, the user either has to make the lambda non-generic, or constrain to only allow types that <code class="language-cpp">day_of_week()</code> accepts, or copy the entire body into a trailing decltype expression. All of these solutions are repetitive and more involved than we thought we needed.
<p>Neither the default <code class="language-cpp">auto</code> return type, nor the expression-agnostic <code class="language-cpp">decltype(auto)</code> as a trailing return type, are adequate solutions. To correctly solve the above issues, our initial wrapping of the overload set as:
<pre><code class="language-cpp">[](auto&& arg) -> decltype(auto) { return foo(arg); }</code></pre>
needs to be extended out all the way to:
<pre><code class="language-cpp">[](auto&& x) -> decltype(twice(std::forward<decltype(x)>(x))) noexcept(noexcept(twice(std::forward<decltype(x)>(x)))) { return twice(std::forward<decltype(x)>(x)); }</code></pre>
Needless to say, this is an unsatisfactory solution. Moreover, it may be insufficient.
<h3>1.3. <code class="language-cpp">decltype(expr)</code> or <code class="language-cpp">decltype((expr))</code>?</h3>
For the most part, there is no difference between <code class="language-cpp">decltype(expr)</code> and <code class="language-cpp">decltype((expr))</code>, they yield the same type for many expressions. The only cases where they yield <i>different</i> types are when <code class="language-cpp">expr</code> is an <i>id-expression</i> or a class member access.
<pre><code class="language-cpp">auto lam1 = [](auto&& x) -> decltype(x) { return x; };
auto lam2 = [](auto&& x) -> decltype((x)) { return x; };
int i;
lam1(i); // ok, returns int& because decltype(x) is int&
lam1(0); // error: can't bind lvalue to int&&
lam2(i); // ok, returns int& because x is an lvalue
lam2(0); // ok, returns int& because x is an lvalue
#define FWD(x) static_cast<decltype(x)&&>(x) // see p0644
struct S { int x; };
auto getx1 = [](auto&& s) -> decltype(FWD(s).x) { return FWD(s).x; };
auto getx2 = [](auto&& s) -> decltype((FWD(s).x)) { return FWD(s).x; };
S s{42};
getx1(s); // returns int
getx1(std::move(s)); // returns int
getx2(s); // returns int&
getx2(std::move(s)); // returns int&&
</code></pre>
For those functions that simply return class member access, <code class="language-cpp">decltype((expr))</code> is more likely to be the desired behavior. But this is a particularly subtle distinction.
<h3>1.4. Potential for improvement</h3>
<p>In C++17 today, in order to lift an expression into a function, whether to be used as a callback or a predicate or a transformation, doing it properly is very difficult and very verbose. We need a trailing return type that wraps the whole expression, with an extra set or parentheses. We need a noexcept specification. This, in of itself, involves writing the function body in triplicate.
<p>Furthermore, lambdas are unique in C++ in that they often are meaningless in a vacuum - they rely on the immediate context that comes from the expressions that contain them and code that precedes them. Since they often appear as sub-expressions, there is intrinsic value in making them shorter - if we can make them shorter by simply removing aspects of them that do not provide meaning to other readers of our code.
<p>These issues arise in the context of function templates and member function templates as well. When the goal is to lift an expression into a function, the unnecessary boilerplate loses all expressiveness. This proposal seeks to eliminate this boilerplate in <i>simple</i> lambdas, function templates, and member function templates, without preventing users from writing the arbitrarily complex lambdas and functions that we can today.
<a name="Proposal"> </a><h2>2. Proposal</h2>
<p>This paper proposes several different enhancements to the language as it relates to lambdas and functions. While we believe that all of these changes have benefits to beginners and experts alike, and we hope to see them all adopted, we understand that they are somewhat orthogonal of each other and in an effort to faciliate discussion and to provide EWG with options, we present them as as a primary proposal and several extensions to it.
<h3>2.1. Primary Proposal: <code>=> <i>assignment-expression</i></code> for lambdas</h3>
<p>This paper proposes the creation of a new function body introducer for lambdas, <code class="language-cpp">=></code>, which allows for a single <i>assignment-expression</i> as the body that will be its return statement. The expression will also be used to synthesize a SFINAE-, Concept-, and <code class="language-cpp">noexcept</code>-friendly trailing return type. The <i>assignment-expression</i> will be used as body the body of the lambda and to determine a SFINAE- and noexcept-friendly return type.
<p>That is, this lambda:
<pre><code class="language-cpp">[](auto&& a, auto&& b) => a.id() < b.id();</code></pre>
shall be treated as if it were written:
<pre><code class="language-cpp">[](auto&& a, auto&& b) -> decltype((a.id() < b.id())) noexcept(noexcept(a.id() < b.id())) { return a.id() < b.id(); }</code></pre>
<p>Note that there is no <code class="language-cpp">return</code> keyword used in the lambda body, it is unnecessary.
<p>Some clarifying examples of the implications of the lambda body being an <i>assignment-expression</i>:
<pre><code class="language-cpp">[&](auto&& x) => x[0](y) // lambda taking a single forwarding reference named x and returns the result of invoking x[0] with y
([&](auto&& x) => x[0])(y) // lambda taking a single forwarding reference named x and returning x[0], immediately invoked with y
// effectively, y[0]
f([](auto&& x) => x, 4) // invoke f with two arguments: an identity lambda and 4
f([](auto&& x) => (x, 4)) // invoke f with one argument: a lambda taking one argument and returning 4
</code></pre>
<p> While having the lambdas in this case have a return type of <code class="language-cpp">auto</code> and no <code class="language-cpp">noexcept</code>-specification is more consistent with the language as it stands, having simple lambdas and simple function templates have a return type of <code class="language-cpp">decltype((expression))</code> and <code class="language-cpp">noexcept</code>-specification of <code class="language-cpp">noexcept(noexcept(expression))</code> saves the user from typing a bunch of repetitive boilerplate, and gets us to the point where we can just write <i>simple</i> code that just does the Right Thing™.
<h3>2.2. Extension: <code>=> <i>assignment-expression</i></code> for functions</h3>
<p>All the same motivation that applies to lambdas applies equally to functions. Hence, we propose that <code>=> <i>assignment-expression</i></code> be allowed for all functions as well. This would allow, for instance, a simplification of <code class="language-cpp">std::begin</code>:
<pre><code class="language-cpp">template <class C> auto begin(C& c) -> decltype(c.begin()) { return c.begin(); } // C++14 today
template <class C> auto begin(C& c) => c.begin(); // this proposal</code></pre>
Except that now <code class="language-cpp">begin</code> would additionally be conditionally <code class="language-cpp">noexcept</code>.
<h3>2.3. Extension: Omitting types in lambda parameter declaration for abbreviated lambdas</h3>
<p>With C++14, <code class="language-cpp">auto&&</code> has become the safe default usage for lambdas. It's almost never wrong. Even if the lambda isn't intended to be used polymorphically, it's preferable to use <code class="language-cpp">auto&&</code> rather than writing out the full name of the type. But once we're always writing <code class="language-cpp">auto&&</code>, it itself doesn't actually have any meaning (if it ever did). Consider a predicate for sorting <code class="language-cpp">lib::Widget</code>s by id:
<pre><code class="language-cpp">// C++11
[](lib::Widget const& a, lib::Widget const& b) { return a.id() < b.id(); }
// C++14 - Widget is simply inferred
[](auto&& a, auto&& b) { return a.id() < b.id(); }
</code></pre>
It's a full third shorter to just use <code class="language-cpp">auto&&</code>, but what does <code class="language-cpp">auto&&</code> gives us at that point? Moreover, in those cases where <code class="language-cpp">auto&&</code> is wrong, it's not easy to distinguish. For instance, it may be important to only accept lvalues. But there is very little difference between a lambda taking its argument by <code class="language-cpp">auto&</code> vs <code class="language-cpp">auto&&</code>. Indeed, it looks more like a typo than a deliberate choice. It may also be important to accept arguments by reference to <code class="language-cpp">const</code> (particularly where threading or COW is concerned). These situations are less common, but it's important to be able to visually distinguish between them in code - and the code we have today is often insufficiently different.
<p>Hence, we propose that use of <code class="language-cpp">=></code> in lambdas, specifically, will allow for omitting types in the parameter list, defaulting to <code class="language-cpp">auto&&</code>. This flips the treatment of a single identifier from identifying an unnamed argument of that type to identifying a forwarding reference with that name:
<pre><code class="language-cpp">[](x, y) { ... } // lambda taking two unnamed parameters of types x and y
[](x, y) => (...) // lambda taking two forwarding references named x and y
[](x& ) => ... // ill-formed
[](auto&&... xs) => f(xs...); // primary proposal: lambda taking pack of forwarding references
[](xs...) => f(xs...); // with this extension
</code></pre>
Which allows the first example of this section to be rewritten as:
<pre><code class="language-cpp">[](auto&& a, auto&& b) { return a.id() < b.id(); } // C++14
[](a, b) => a.id() < b.id() // this proposal</code></pre>
<p>While types <i>may be</i> omitted, it is not mandatory.
<h3>2.4. Implementation Experience</h3>
An implementation of this proposal for gcc has been graciously provided by Bastien Penavayre on <a href="https://github.com/DaemonSnake/gcc-abbreviated-cpp-lambda">github</a>, along with a version of the <a href="http://www.gcc-abbreviated-lambdas-proposal.tk/">compiler explorer</a>.
<a name="Examples"></a><h2>3. Examples</h2>
Transparently binding an overloaded member functions <code class="language-cpp">func</code> to an instance <code class="language-cpp">obj</code>:
<pre><code class="language-cpp">// C++14
[&](auto&&... args) noexcept(noexcept(obj.func(std::forward<decltype(args)>(args)...)))
-> decltype((obj.func(std::forward<decltype(args)>(args)...))) {
return obj.func(std::forward<decltype(args)>(args)...);
}
// this proposal
[&](args...) => obj.func(std::forward<decltype(args)>(args)...)
// with p0644
[&](args...) => obj.func(>>args...)
</code></pre>
<p>Sorting in decreasing order. Currently, we would rely on one of the many <i>named</i> function objects in the standard library to do this for us. While we recommend programmers use the standard algorithms, the standard function objects aren't quite on that level. This proposal allows us to just use <code class="language-cpp">></code>. It's longer, but arguably clearer:
<pre><code class="language-cpp">std::sort(v.begin(), v.end(), std::greater{}); // C++17
std::sort(v.begin(), v.end(), [](x,y) => x > y); // this proposal</code></pre>
Once we move from directly working on the elements to working on other functions of the elements, the gain becomes much bigger. Sorting in decreasing order by ID is both shorter and much more readable:
<pre><code class="language-cpp">std::sort(v.begin(), v.end(), [](auto&& x, auto&& y) { return x.id() > y.id(); }); // C++14
std::sort(v.begin(), v.end(), std::greater{}, &lib::Widget::id); // Ranges TS, with projection*
// ... assuming id isn't overloaded
std::sort(v.begin(), v.end(), std::greater{}, [](auto&& w) -> decltype(auto) { return w.id(); });
// Ranges TS, if id() is overloaded and returns
// an expensive-to-copy type. Don't forget decltype(auto)!
std::sort(v.begin(), v.end(), [](x,y) => x.id() > y.id()); // this proposal
</code></pre>
Transforming a vector into another vector using a map as the function. Here, it's important to avoid the extra copy, and this proposal makes it easy to do so:
<pre><code class="language-cpp">std::transform(v.begin(), v.end(), std::back_inserter(v2),
[&](auto&& key) { return map.at(key); }); // C++17: woops, extra copies!
std::transform(v.begin(), v.end(), std::back_inserter(v2),
[&](auto&& key) -> decltype(auto) { return map.at(key); }); // C++17
std::transform(v.begin(), v.end(), std::back_inserter(v2),
[&](key) => map.at(key)); // this proposal</code></pre>
Check if all of the elements have some predicate satisfied - by element directly:
<pre><code class="language-cpp">std::all_of(v.begin(), v.end(), [](auto&& elem) { return elem.done(); }); // C++14, direct
std::all_of(v.begin(), v.end(), std::mem_fn(&lib::Element::done)); // C++14, with pointers to member functions*
std::all_of(v.begin(), v.end(), [](e) => e.done()); // this proposal
</code></pre>or on an external object:
<pre><code class="language-cpp">std::all_of(v.begin(), v.end(), [&](auto&& elem) { return obj.satisfied(elem); }); // C++14, directly
std::all_of(v.begin(), v.end(), std::bind(&lib::Element::satisfied, std::ref(obj), _1); // C++14, with std::bind*
std::all_of(v.begin(), v.end(), [&](elem) => obj.satisfied(elem)); // this proposal
</code></pre>
Looking for an element by id:
<pre><code class="language-cpp">auto it = std::find_if(v.begin(), v.end(), [&](auto&& elem) { return elem.id() == id; }); // C++14
auto it = std::find(v.begin(), v.end(), id, &lib::Widget::id); // Ranges TS, with projection*
auto it = std::find_if(v.begin(), v.end(), [&](elem) => elem.id() == id); // this proposal</code></pre>
Number of pairwise matches between two containers, using <code class="language-cpp">std::inner_product</code> with two callables. With this proposal, it's actually a little longer, but importantly you don't have to know the names of things - you just write the expressions you want to write:
<pre><code class="language-cpp">int matches = std::inner_product(as.begin(), as.end(), bs.begin(), 0, // C++14, example from cppreference.com
std::plus<>(), std::equal_to<>());
int matches = std::inner_product(as.begin(), as.end(), bs.begin(), 0, // this proposal
[](a,b) => a + b, [](a,b) => a == b);</code></pre>
<p>Writing some overloads of getters:
<table style="width:100%"><tr><th style="width:50%">C++17</th><th>This proposal</th></tr>
<tr><td><pre style="background:transparent;border:0px"><code class="language-cpp">
T& get() & { return value; }
T const& get() const& { return value; }
T&& get() && { return std::move(value); }
T const&& get() const&& { return std::move(value); }</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">
auto get() & => value;
auto get() const& => value;
auto get() && => std::move(value);
auto get() const&& => std::move(value);</code></pre></td>
</tr></table>
<p>Writing a negating function object:
<table style="width:100%">
<tr><th style="width:50%">C++17</th><th>This proposal</th></tr>
<tr><td><pre style="background:transparent;border:0px"><code class="language-cpp">template<typename F>
class variadic_negator_functor
{
F f_;
public:
explicit variadic_negator_functor(F a_f) : f_(a_f) {}
template<typename... Args>
auto operator()(Args&&... args)
-> decltype(!this->f_(std::forward<Args>(args)...))
noexcept(noexcept(!this->f_(std::forward<Args>(args)...)))
{ return !this->f_(std::forward<Args>(args)...); }
template<typename... Args>
auto operator()(Args&&... args) const
-> decltype(!this->f_(std::forward<Args>(args)...))
noexcept(noexcept(!this->f_(std::forward<Args>(args)...)))
{ return !this->f_(std::forward<Args>(args)...); }
// ...
};</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">template<typename F>
class variadic_negator_functor
{
F f_;
public:
explicit variadic_negator_functor(F a_f) : f_(a_f) {}
template<typename... Args>
auto operator()(Args&&... args)
=> !this->f_(std::forward<Args>(args)...);
template<typename... Args>
auto operator()(Args&&... args) const
=> !this->f_(std::forward<Args>(args)...);
// ...
};</code></pre></td>
</table>
<p>Having to use nested lambdas. Dropping one <code class="language-cpp">return</code> might not seem like much, until you need to do it multiple times, which comes up quite often in functional programming. Consider this selection of examples from <a href="https://github.com/boostorg/hana/tree/1873a828c5c44ec853d465c7ab0c7cb2f821d44b/include/boost/hana/functional">Boost.Hana</a>, courtesy of Louis Dionne (with some minor tweaks):
<table style="width:150%">
<tr><th style="width:33.33%">C++17</th><th style="width:33.33%">This proposal</th><th style="width:33.33%">This proposal + p0644</th></tr>
<tr><td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto always = [](auto x) {
return [x(std::move(x))](auto const& ...y) -> decltype((x)) {
return x;
};
};</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto always = [](auto x) =>
[x=std::move(x)](y...) => x;</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto always = [](x) => [x=>>x](y...) => x;</code></pre></td>
</tr>
<tr><td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto apply = [](auto&& f, auto&& ...x) -> decltype(auto) {
return std::forward<decltype(f)>(f)(
std::forward<decltype(x)>(x)...
);
};</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto apply = [](f, xs...) =>
std::forward<decltype(f)>(f)(
std::forward<decltype(xs)>(xs)...);</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto apply = [](f, xs...) => (>>f)(>>xs...);</code></pre></td>
</tr>
<tr><td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto compose2 = [](auto f, auto g) -> decltype(auto) {
return [f(std::move(f)), g(std::move(g))]
(auto&& x, auto&& ...xs) -> decltype(auto) {
return f(
g(std::forward<decltype(x)>(x)),
std::forward<decltype(xs)>(xs)...
);
};
};</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto compose2 = [](auto f, auto g) =>
[f=std::move(f), g=std::move(g)](x, xs...) =>
f(g(std::forward<decltype(x)>(x)),
std::forward<decltype(xs)>(xs)...);</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto compose2 = [](f, g) =>
[f=>>f, g=>>g](x, xs...) => f(g(>>x), >>xs...);
</tr>
<tr><td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto partial = [](auto f, auto ...x) {
return [f(std::move(f)), x...](auto&& ...y) -> decltype(auto) {
return f(x..., std::forward<decltype(y)>(y)...);
};
};</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto partial = [](auto f, auto... x) =>
[f=std::move(f), x...](y...) =>
f(x..., std::forward<decltype(y)>(y)...);</code></pre></td>
<td><pre style="background:transparent;border:0px"><code class="language-cpp">constexpr auto partial = [](f, x...) =>
[f=>>f, x=>>x...](y...) => f(x..., >>y...);
//with the addition of p0780, can forward the whole pack
//instead of having to copy</code></pre></td></tr>
</table>
<p>In all of these cases, the function body - the expression we're trying to use - is really simple. Which is the point. Let's make it simpler to write simpler things.
<a name="Hyper"> </a><h2>4. Hyper-abbreviated lambdas</h2>
<p>This proposal is about as abbreviated as you can get, without loss of clarity or functionality. But we can always go deeper. Any proposal on abbreviating lambdas would be incomplete without mentioning that numerous languages (including, but not limited to Swift, Elixir, and Scala), as well as the Boost.Lambda library, allow for writing expressions that themselves are callable. These refer to arguments by number and then synthesize a new closure. An example using Swift's syntax with the sorting by id predicate that we have been using throughout:
<pre><code class="language-cpp">[](auto&& a, auto&& b) { return a.id() < b.id(); } // C++14 (51 characters)
[](a, b) => a.id() < b.id() // this proposal (28)
$1.id() < $2.id() // even more abbreviation (18)</code></pre>
or checking if an element satsifes an object's test:
<pre><code class="language-cpp">[&](auto&& elem) { return obj.satisfies(elem); }; // C++14 (50 characters)
[&](elem) => obj.satisfies(elem); // this proposal (34)
obj.satisfies($1); // even more abbreviation (19)</code></pre>
<p>While <code class="language-cpp">$</code> directly is probably not the best choice in C++, there are other characters currently unusable in this context whose meaning we could overload here (e.g. <code class="language-cpp">&</code>). In our opinion, this next step in abbreviation is unnecessary as this proposal gets you most of the way there and now we start moving towards a brevity that sacrifices some clarity.
<p>Additionally, an earlier draft of this paper had proposed omitting the lambda capture entirely - with missing capture defaulting to <code class="language-cpp">[&]</code>. This would allow the
shortening the lambda <code class="language-cpp">[&](e) => obj.satisfies(e)</code> to <code class="language-cpp">(e) => obj.satisfies(e)</code> and possibly even to <code class="language-cpp">e => obj.satisfies(e)</code>. It has been pointed out that this would be quite difficult to parse for compilers, and it has relatively marginal benefit compared to the other proposed changes, so it has been removed.
<a name="Priors"></a><h2>5. Prior Work and Effects on Existing Code</h2>
<p>The original paper introducing what are now generic lambdas [2] also proposed extensions for omitting the <i>type-specifier</i> and dropping the body of a lambda if it's a single expression. This paper provides a different path towards those that same goal. Another paper proposing the same abbreviating syntax [4] has been merged into this one.
<p>The usage of <code class="language-cpp">=></code> (or the similar <code class="language-cpp">-></code>) in the context of lambdas appears in many, many programming languages of all varieties. A non-exhaustive sampling: C#, D, Erlang, F#, Haskell, Idris, Java, JavaScript, ML, OCaml, Swift. The widespread use is strongly suggestive that the syntax is easy to read and quite useful.
<p>The sequence of characters <code class="language-cpp">=></code> can appear in code in rare scenarios, such as passing the address of the assignment operator as a template non-type argument: <code class="language-cpp">X<&Y::operator=></code>. However, such usage is incredibly rare, so this proposal would have very limited effect on existing code. Thanks to Richard Smith for doing a search.
<a name="Revisions"> </a><h2>6. Revision History</h2>
<p>Since r0, this paper focused on a single syntax for abbreviating lambdas, and includes a stronger motivation for the need for SFINAE and <code class="language-cpp">noexcept</code>. The section on abbreviated forwarding has been pulled out into its own paper [3], and a new section has been introduced discussing expression-based lambdas.
<p>Since r1, this paper has combined with P0238 [4], changing the proposed SFINAE-friendly syntax to deducing <code class="language-cpp">decltype((expr))</code> instead of <code class="language-cpp">decltype(expr)</code> and also applying to functions. This proposal also drops omitting the capture syntax, and splits out each of the desired outcomes as extensions from the core proposal of <code class="language-cpp">=> expr</code> for lambdas.
<a name="Acks"></a><h2>7. Acknowledgements and References</h2>
<p>Thanks to Andrew Sutton for considering and rejecting several bad iterations of this proposal. Thanks to Richard Smith and Daveed Vandevoorde for looking into the practicality of this design along with parsing issues. Thanks to Nicol Bolas for refocusing the paper. Thanks to John Shaw for putting up with many crazy ideas.
<p>Thanks especially to Adam Martin for presenting this proposal at Kona, and Nathan Myers for valuable feedback. Thanks to Casey Carter for presenting p0238 in Toronto and the valuable feedback from there.
<p>Thanks to Bastien Penavayre for providing an implementation for gcc.
<p> [1] <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0119r2.pdf">Overload sets as function arguments</a>
<p> [2] <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf">Proposal for Generic (Polymorphic) Lambda Expressions</a>
<p> [3] <a href="http://wg21.link/p0644">Forward without forward</a>
<p> [4] <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0238r1.html">Return type deduction and SFINAE</a>
</body>
</html>