www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Future of string lambda functions/string predicate functions

reply "Jakob Ovrum" <jakobovrum gmail.com> writes:

of string lambda functions has again cropped up. I think we 
should clearly decide on some things regarding them. Questions 
such as; are they a worthwhile alternative in the present 
language? Should they be deprecated? Should they be supported in 
new additions to Phobos?

Some background: string lambda functions are a feature of 
std.functional/std.range/std.algorithm, where strings can be 
passed in lieu of functions as template alias arguments to 
various public functions in these modules. The string becomes the 
return expression of an anonymous function which implicitly has 
one or two arguments, named "a" and "b", like the string "a < 3" 
in the following example expression:

   arr.filter!"a < 3"

When this feature was developed, there were no lambda function 
literals in the language. There were anonymous functions, but 
their syntactical overhead means they fare poorly as a 
replacement for lambda functions:

   arr.filter!((a) { return a < 3; })

The effect is particularly jarring in bigger compositions:

   assert([ 1, 2, 3, 4, 5 ].filter!((a) { return a < 3; 
}).equal([1, 2]));

Since then, a first-class lambda syntax has been introduced, with 
significantly less syntactic overhead:

   arr.filter!(a => a < 3)

The issue is then: is the old string lambda syntax obsolete in 
the face of the new lambda literals?

----------

My opinion on the matter is that the *only* advantage of string 
lambdas is that they are (even) shorter than the new lambda 
syntax. However, I don't think that comes even close to making up 
for its many disadvantages; here are the ones I think are the 
biggest:

  * The number one reason string lambdas are shorter is because 
they implicitly introduce their parameters. However, this means 
that you're always stuck with the generic, uninformative 
parameter names "a" and "b".
  * To the uninitiated, they may not look like code. They 
certainly aren't highlighted as D code in your average code 
editor (the q{} literal deserves a mention, but then some of the 
succinctness advantage is lost and it's not just for D code), and 
the magically, implicitly introduced variables "a" and "b" is 
something you plain just have to know beforehand to understand.
  * Apart from "a" and "b", they can only access symbols that are 
visible in the scope of the std.functional module.

In light of the above points, I just find them excessively arcane 
- something new D programmers shouldn't have to learn and thus 
none of us should continue to use or promote. The symbols you can 
access in a string lambda are determined by the implementation 
details of std.functional and thus, may change at any time. It is 
then only reasonable to recommend that the programmer should not 
depend on any symbols except for "a" and "b", severely limiting 
the utility of string lambdas. Also, they only exist in unary and 
binary forms at present, which means new functions with different 
requirements cannot leverage them. And then comes the point about 
consistency; if the utility of string lambdas is so limited in 
the face of the general lambda literal syntax, foregoing string 
lambdas in favour of lambda literals helps code consistency.

I can also think of some relatively minor disadvantages of string 
lambdas, such as the compile-time performance cost they incur.

I think string lambdas were a valiant effort to fill a glaring 
hole in the language at the time, but are now superseded by an 
overall superior alternative. The cognitive and maintenance load 
on programmers is not worth their marginal utility. I think we 
should deprecate string lambdas altogether.

Specifically, I suggest the following deprecation path:

  * Add deprecation notes to std.functional's unaryFun and 
binaryFun so users are dissuaded from using them in new code. In 
time, we would remove their documentation.
  * Leave support for string lambdas in existing Phobos functions 
for the foreseeable future, for backwards-compatibility purposes.
  * Change all documentation so that it doesn't mention string 

(Switch std.algorithm/.range to lambda syntax)[2] attempted this 
and was approved and merged, but subsequently reverted due to 
bugs.
  * New functions would not support string lambdas.

----------

To be honest, I thought this was a foregone conclusion, but 
comments on Github indicate otherwise, hence this thread.

What does everyone think?

  [1] https://github.com/D-Programming-Language/phobos/pull/1453
  [2] https://github.com/D-Programming-Language/phobos/pull/707
Aug 06 2013
next sibling parent "qznc" <qznc web.de> writes:
On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:

 of string lambda functions has again cropped up. I think we 
 should clearly decide on some things regarding them. Questions 
 such as; are they a worthwhile alternative in the present 
 language? Should they be deprecated? Should they be supported 
 in new additions to Phobos?

 What does everyone think?
I was certainly irritated, when I used the std.functional stuff the first time. Code generation through strings is kind of a last resort to me. It should be avoided if possible. Since it is possible now, I would vote for deprecating string functions.
Aug 06 2013
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jakob Ovrum:

 What does everyone think?
In this page: http://dlang.org/phobos/std_algorithm.html There is written: bool isSorted(alias less = "a < b", Range)(Range r); If you remove string lambdas, how is that signature? My opinion is that lambdas are more clean, and I usually prefer them, but in certain situations I prefer string lambdas. So my vote is -0. Bye, bearophile
Aug 06 2013
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 6 August 2013 at 09:51:07 UTC, bearophile wrote:
 In this page:
 http://dlang.org/phobos/std_algorithm.html

 There is written:
 bool isSorted(alias less = "a < b", Range)(Range r);

 If you remove string lambdas, how is that signature?
bool isSorted(alias less = (a, b) => a < b, Range)(Range r);
 My opinion is that lambdas are more clean, and I usually prefer 
 them, but in certain situations I prefer string lambdas.
I think those few situations should forego string lambdas in favour of reduced cognitive load and increased consistency.
Aug 06 2013
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 6 August 2013 at 10:05:54 UTC, Jakob Ovrum wrote:
 I think those few situations should forego string lambdas in 
 favour of reduced cognitive load and increased consistency.
To clarify: those are the benefits we gain under the presumption that string lambdas are not inherently flawed. I personally believe that they *are* - as long as string lambdas magically introduce symbols and are limited to an arbitrary selection of accessible symbols, I don't think it's reasonable to use them in *any* code.
Aug 06 2013
next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 06.08.2013 12:09, schrieb Jakob Ovrum:
 On Tuesday, 6 August 2013 at 10:05:54 UTC, Jakob Ovrum wrote:
 I think those few situations should forego string lambdas in
 favour of reduced cognitive load and increased consistency.
To clarify: those are the benefits we gain under the presumption that string lambdas are not inherently flawed. I personally believe that they *are* - as long as string lambdas magically introduce symbols and are limited to an arbitrary selection of accessible symbols, I don't think it's reasonable to use them in *any* code.
100% correct - string lambdas behave just not very generic, at all but normal lambdas do, with the big disadvantage of a little bit more keyboard grinding :)
Aug 06 2013
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 6 August 2013 at 10:22:53 UTC, dennis luehring wrote:
 but normal lambdas do, with the big disadvantage of a little 
 bit more keyboard grinding :)
It's a "big disadvantage" only if you're following the Perl school of programming :P
Aug 06 2013
parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 06.08.2013 12:49, schrieb Jakob Ovrum:
 On Tuesday, 6 August 2013 at 10:22:53 UTC, dennis luehring wrote:
 but normal lambdas do, with the big disadvantage of a little
 bit more keyboard grinding :)
It's a "big disadvantage" only if you're following the Perl school of programming :P
according to https://github.com/D-Programming-Language/phobos/pull/707 there where errors and everything was just reverted (without further analysis?) - any idea about the current state
Aug 06 2013
parent "Brad Anderson" <eco gnuk.net> writes:
On Tuesday, 6 August 2013 at 10:52:06 UTC, dennis luehring wrote:
 Am 06.08.2013 12:49, schrieb Jakob Ovrum:
 On Tuesday, 6 August 2013 at 10:22:53 UTC, dennis luehring 
 wrote:
 but normal lambdas do, with the big disadvantage of a little
 bit more keyboard grinding :)
It's a "big disadvantage" only if you're following the Perl school of programming :P
according to https://github.com/D-Programming-Language/phobos/pull/707 there where errors and everything was just reverted (without further analysis?) - any idea about the current state
More details from it being reopened: https://github.com/D-Programming-Language/phobos/pull/744
Aug 06 2013
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, August 06, 2013 12:09:25 Jakob Ovrum wrote:
 On Tuesday, 6 August 2013 at 10:05:54 UTC, Jakob Ovrum wrote:
 I think those few situations should forego string lambdas in
 favour of reduced cognitive load and increased consistency.
To clarify: those are the benefits we gain under the presumption that string lambdas are not inherently flawed. I personally believe that they *are* - as long as string lambdas magically introduce symbols and are limited to an arbitrary selection of accessible symbols, I don't think it's reasonable to use them in *any* code.
So, don't use them with functions. Just use them with operators. They're fantastic for that and far less verbose than the newer lambda syntax is for the same code. - Jonathan M Davis
Aug 06 2013
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jakob Ovrum:

 My opinion is that lambdas are more clean, and I usually 
 prefer them, but in certain situations I prefer string lambdas.
I think those few situations should forego string lambdas in favour of reduced cognitive load and increased consistency.
In Scala they sometimes use lambdas where arguments are underscores (http://stackoverflow.com/questions/7673545/usage-of-in-scala-lambda-functions ). It's not good to give names to lambda arguments every time. In a program it's useful to give names to variables to make them more readable, but if you look at Haskell code you see several variable names like "a", "b", etc. They use such variable names not because they are lazy people, but because you are writing a very generic function, and very short names are good to remind you they are not important for their semantics, but for other things, like their position. Using default names as 'a' and 'b' for very simple lambdas is nice. And "(a, b) => a > b)" doesn't give more information than "q{a > b}". In functional-style code it's important to have means, when you desire it, to write compact code. So I'd like to keep the string lambdas. Bye, bearophile
Aug 06 2013
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 7 August 2013 at 00:54:10 UTC, bearophile wrote:
 In Scala they sometimes use lambdas where arguments are 
 underscores 
 (http://stackoverflow.com/questions/7673545/usage-of-in-scala-lambda-functions 
 ).

 It's not good to give names to lambda arguments every time. In 
 a program it's useful to give names to variables to make them 
 more readable, but if you look at Haskell code you see several 
 variable names like "a", "b", etc. They use such variable names 
 not because they are lazy people, but because you are writing a 
 very generic function, and very short names are good to remind 
 you they are not important for their semantics, but for other 
 things, like their position.
I recognize the existence of code where symbol names like "a" and "b" are perfectly justifiable. However, I think they are in the minority and should not be encouraged for the average case, whether by language or library. I also think that names like "a" and "b" probably appear more in library code than application code, particularly libraries with wide utility such as Phobos, which further leads me to believe that they should not be implicitly encouraged.
 Using default names as 'a' and 'b' for very simple lambdas is 
 nice.
I strongly disagree; in functional compositions in user code, I think "a" and "b" are most commonly justified when doing numeric operations, which is only a subset of what the generic algorithms in Phobos aim to facilitate. In the general case, more specific identifier names are desirable.
 And "(a, b) => a > b)" doesn't give more information than "q{a 
 b}".
I'm not so sure. The lambda literal syntax is a universally applicable language construct that most D programmers will come to know; I don't know if I can say the same about string lambdas. q{}-style string literals are not just for D source code, so syntax highlighting may or may not treat it as such. With the lambda literal syntax, the names "a" and "b" are not part of any implicit context and have no exceptional meaning - with a few notable exceptions such as the implicit `this` parameter, explicitly introduced symbols are the norm in D. In some languages, such as Go, one can see a reminiscent style of OOP where even the `this` parameter is explicit. I'm not familiar with the full reasoning behind this so it may be a complete red herring, but I think it's interesting to note.
 In functional-style code it's important to have means, when you 
 desire it, to write compact code.
I completely agree, but I argue that string lambdas enable you to compact your code by means of obscurity, which is counter-productive, assuming the goal is to write more readable code.
Aug 06 2013
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Aug 06, 2013 at 12:05:53PM +0200, Jakob Ovrum wrote:
 On Tuesday, 6 August 2013 at 09:51:07 UTC, bearophile wrote:
In this page:
http://dlang.org/phobos/std_algorithm.html

There is written:
bool isSorted(alias less = "a < b", Range)(Range r);

If you remove string lambdas, how is that signature?
bool isSorted(alias less = (a, b) => a < b, Range)(Range r);
In this case, I'll have to be honest and say that the string lambda is much more readable.
My opinion is that lambdas are more clean, and I usually prefer
them, but in certain situations I prefer string lambdas.
I think those few situations should forego string lambdas in favour of reduced cognitive load and increased consistency.
I thought about this a bit this morning. Jonathan definitely has a point about string lambdas being more readable, but OTOH, it does increase cognitive load, and it suffers from incompleteness (can't reference symbols not visible from std.functional -- how would anybody know that if they don't know how Phobos is implemented??). The new syntax is slightly more cumbersome, and perhaps a little harder to read due to unfamiliarity. But in the end, all things considered, I think I'll have to concede that using lambda syntax is better. Here are my points of consideration for/against using lambda syntax: -1: They are less readable than string lambdas. -1: They require the cognitive load of changing coding style for current D coders who are already used to string lambdas (though this isn't *that* big a minus, considering that string lambdas will still be supported in existing code, just no longer documented / encouraged). +1: They can reference any symbol visible in the local scope, instead of depending on implementational details like visibility in std.functional (which may change between releases!) +1: They explicitly declare their parameters, and are therefore easier for a total newbie to understand (the first time I saw a string lambda, I went, where on earth do those "a"s and "b"s come from?! is that a typo in the docs?!). They also let you use more self-documenting argument names, like (start,end) => end-start, instead of the totally opaque "b-a". They also let you specify argument types for readability: (Config c, int val) => c.add(val), instead of the mysterious "a.add(b)" in which the types of a and b are unclear. +1: They are more consistent with other places were lambdas are used (consider a newbie going, why doesn't `auto f = "a<b";` give me a lambda function, it worked when I pass that to std.algorithm?) +1: They avoid having Yet Another Thing newbies have to learn to be able to use D effectively. This will help D adoption, at basically no cost except a small inconvenience to a small number of long-time D veterans. D already is pushing the envelope on its sheer number of features; it's a good thing to keep the number of *variations* per feature to a minimum. So in the end, I have to reluctantly conclude that deprecating string lambdas is probably the best way to go. T -- EMACS = Extremely Massive And Cumbersome System
Aug 06 2013
prev sibling parent "Wyatt" <wyatt.epp gmail.com> writes:
On Tuesday, 6 August 2013 at 09:51:07 UTC, bearophile wrote:
 So my vote is -0.
Curse you, flush-to-zero denormal mode! ;) OP: To be honest, I didn't even know about string lambdas. They don't seem to be a well-advertised, hot-button feature. Just looking at the examples, I rather think they're hard for humans to parse, and wouldn't even notice if they were quietly dragged out back and exorcised. -Wyatt
Aug 06 2013
prev sibling next sibling parent "Andre Artus" <andre.artus gmail.com> writes:
I would vote for marking it as  deprecated, and clearly 
indicating it as such in the docs.
Aug 06 2013
prev sibling next sibling parent reply "Meta" <jared771 gmail.com> writes:
On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:
  * Add deprecation notes to std.functional's unaryFun and 
 binaryFun so users are dissuaded from using them in new code. 
 In time, we would remove their documentation.
Looks good except for the above point. UnaryFun and binaryFun still have valid use cases, and I'd argue that it's even worth making an nAryFun!(pred, arity). import std.functional; import std.stdio; alias less = (int a, int b) => a < b; //Error alias less = binaryFun!((a, b) => a < b); //Ok void main() { writeln(less(1, 2)); //True }
Aug 06 2013
parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Tuesday, 6 August 2013 at 19:38:08 UTC, Meta wrote:
 Looks good except for the above point. UnaryFun and binaryFun 
 still have valid use cases, and I'd argue that it's even worth 
 making an nAryFun!(pred, arity).

 import std.functional;
 import std.stdio;

 alias less = (int a, int b) => a < b; //Error
 alias less = binaryFun!((a, b) => a < b); //Ok

 void main()
 {
     writeln(less(1, 2)); //True
 }
This is a good point. I also agree that a fully generic, n-ary wrapper is the way to go for these cases. I don't like the name `nAryFun` though, hopefully there's a more generic name that doesn't dwell on the history that would be `unaryFun` and `binaryFun`.
Aug 06 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/07/2013 06:18 AM, Jakob Ovrum wrote:
 On Tuesday, 6 August 2013 at 19:38:08 UTC, Meta wrote:
 Looks good except for the above point. UnaryFun and binaryFun still
 have valid use cases, and I'd argue that it's even worth making an
 nAryFun!(pred, arity).

 import std.functional;
 import std.stdio;

 alias less = (int a, int b) => a < b; //Error
 alias less = binaryFun!((a, b) => a < b); //Ok

 void main()
 {
     writeln(less(1, 2)); //True
 }
This is a good point. I also agree that a fully generic, n-ary wrapper is the way to go for these cases.
I think the way to go for these cases is to fix the parser.
 I don't like the name `nAryFun` though, hopefully there's a more generic
 name that doesn't dwell on the history that would be `unaryFun` and
 `binaryFun`.
Aug 07 2013
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 7 August 2013 at 11:16:05 UTC, Timon Gehr wrote:
 I think the way to go for these cases is to fix the parser.
I'd be very happy with such a change, but to my understanding, it's not just a parser issue. Perhaps a DIP that describes exactly how alias should work and what it should accept is in order for such a change.
Aug 07 2013
prev sibling next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:
 Specifically, I suggest the following deprecation path:

  * Add deprecation notes to std.functional's unaryFun and 
 binaryFun so users are dissuaded from using them in new code. 
 In time, we would remove their documentation.
  * Leave support for string lambdas in existing Phobos 
 functions for the foreseeable future, for 
 backwards-compatibility purposes.
  * Change all documentation so that it doesn't mention string 

 (Switch std.algorithm/.range to lambda syntax)[2] attempted 
 this and was approved and merged, but subsequently reverted due 
 to bugs.
  * New functions would not support string lambdas.
Yes x 4. I think this is the perfect path to their semi-deprecation. Deprecating them completely, I think, would be unwise since there's a lot of code out there using them. Deprecation through obscurity while retaining backwards-compatibility is the right choice.
Aug 06 2013
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Tuesday, 6 August 2013 at 20:28:59 UTC, Peter Alexander wrote:
 On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:
 Specifically, I suggest the following deprecation path:

 * Add deprecation notes to std.functional's unaryFun and 
 binaryFun so users are dissuaded from using them in new code. 
 In time, we would remove their documentation.
 * Leave support for string lambdas in existing Phobos 
 functions for the foreseeable future, for 
 backwards-compatibility purposes.
 * Change all documentation so that it doesn't mention string 

 (Switch std.algorithm/.range to lambda syntax)[2] attempted 
 this and was approved and merged, but subsequently reverted 
 due to bugs.
 * New functions would not support string lambdas.
Yes x 4. I think this is the perfect path to their semi-deprecation. Deprecating them completely, I think, would be unwise since there's a lot of code out there using them. Deprecation through obscurity while retaining backwards-compatibility is the right choice.
I agree this is the best decision.
Aug 06 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 6 August 2013 at 21:35:05 UTC, John Colvin wrote:
 On Tuesday, 6 August 2013 at 20:28:59 UTC, Peter Alexander 
 wrote:
 On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:
 Specifically, I suggest the following deprecation path:

 * Add deprecation notes to std.functional's unaryFun and 
 binaryFun so users are dissuaded from using them in new code. 
 In time, we would remove their documentation.
 * Leave support for string lambdas in existing Phobos 
 functions for the foreseeable future, for 
 backwards-compatibility purposes.
 * Change all documentation so that it doesn't mention string 

 (Switch std.algorithm/.range to lambda syntax)[2] attempted 
 this and was approved and merged, but subsequently reverted 
 due to bugs.
 * New functions would not support string lambdas.
Yes x 4. I think this is the perfect path to their semi-deprecation. Deprecating them completely, I think, would be unwise since there's a lot of code out there using them. Deprecation through obscurity while retaining backwards-compatibility is the right choice.
I agree this is the best decision.
I actual think this is a very bad situation. This puts them in the odd spot of "not deprecated, but sometimes supported". This puts the end coder who *wants* to use lambdas (after all they aren't deprecated), in a weird position of "which functions support it, which functions don't?" Since the end coder is not a D guru, he'll have no idea which functions do support string lambdas, and which don't, which will leave him in an eternal guessing game of "well, looks like *that* doesn't compile"... So, if we *do* choose that string lambdas should leave, then I believe they should clearly be marked as "deprecated". We can leave them in forever, but the end user *must* be told that string lambdas are *not* the way it is meant to be used anymore. -------- Related: My vote goes that they should stay. Sure, normal lambdas are more powerful, but string lambdas are very light weight. Do you really argue that this is better? sort!((a, b) => a > b)(1, 2, 3); vs sort!"a > b"(1, 2, 3); string lambdas look clearer to me. Also, and this is important (!): A lambda is *always unique*, whereas strings alias each other. This means that: sort!"a > b"(1, 2, 3); //Both generate the same template sort!"a > b"(1, 2, 3); // sort!((a, b) => a > b)(1, 2, 3); //Generates two different templates sort!((a, b) => a > b)(1, 2, 3); This is particularly relevant for *struct* that take preds. Imagine: Sorter!"a > b" sorter1; Sorter!"a > b" sorter2; static assert(typeof(sorter1) == typeof(sorter2)); //Passes But Sorter!((a, b) => a > b) sorter1; Sorter!((a, b) => a > b) sorter2; static assert(typeof(sorter1) == typeof(sorter2)); //Fails -------- So, my vote is that string lambdas should stay. Supporting them is easy: Just an extra alias at the top of the function. Getting rid of them buys us nothing. There are people who use them, and there are cases where they make more sense. I *AM* 100% perfectly fine with making the default pred a normal lambda though, and promote their use over string lambdas.
Aug 07 2013
next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Wednesday, 7 August 2013 at 09:12:41 UTC, monarch_dodra wrote:
 Since the end coder is not a D guru, he'll have no idea which 
 functions do support string lambdas, and which don't, which 
 will leave him in an eternal guessing game of "well, looks like 
 *that* doesn't compile"...
String lambdas won't be documented anywhere, so any newcomer to D hopefully won't even know they exist, so this problem won't happen.
 So, if we *do* choose that string lambdas should leave, then I 
 believe they should clearly be marked as "deprecated". We can 
 leave them in forever, but the end user *must* be told that 
 string lambdas are *not* the way it is meant to be used anymore.
Deprecating them (eventually) breaks existing code, which is unacceptable IMO.
 So, my vote is that string lambdas should stay. Supporting them 
 is easy: Just an extra alias at the top of the function. 
 Getting rid of them buys us nothing.
It simplifies the standard library, and makes it easier to write higher-order functions and ranges.
Aug 07 2013
prev sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 7 August 2013 at 09:12:41 UTC, monarch_dodra wrote:
 Also, and this is important (!): A lambda is *always unique*, 
 whereas strings alias each other. This means that:

 sort!"a > b"(1, 2, 3); //Both generate the same template
 sort!"a > b"(1, 2, 3); //

 sort!((a, b) => a > b)(1, 2, 3); //Generates two different 
 templates
 sort!((a, b) => a > b)(1, 2, 3);

 This is particularly relevant for *struct* that take preds.

 Imagine:
 Sorter!"a > b" sorter1;
 Sorter!"a > b" sorter2;
 static assert(typeof(sorter1) == typeof(sorter2)); //Passes

 But
 Sorter!((a, b) => a > b) sorter1;
 Sorter!((a, b) => a > b) sorter2;
 static assert(typeof(sorter1) == typeof(sorter2)); //Fails
Ew. I don't see this as a good argument. Code and strings are fundamentally different. All the following functionally equivalent string lambdas will produce different instantiations of Sorter: "a > b" "b < a" "a<b" " a < b" etc.
Aug 07 2013
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Wednesday, 7 August 2013 at 10:03:58 UTC, Jakob Ovrum wrote:
 All the following functionally equivalent string lambdas will 
 produce different instantiations of Sorter:

     "a > b"
     "b < a"
     "a<b"
     " a < b"

 etc.
Oops, accidentally reversed it there. Correction: "a > b" "b < a" "a>b" " a > b"
Aug 07 2013
prev sibling next sibling parent "Dejan Lekic" <dejan.lekic gmail.com> writes:
On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:

 of string lambda functions has again cropped up. I think we 
 should clearly decide on some things regarding them. Questions 
 such as; are they a worthwhile alternative in the present 
 language? Should they be deprecated? Should they be supported 
 in new additions to Phobos?

 Some background: string lambda functions are a feature of 
 std.functional/std.range/std.algorithm, where strings can be 
 passed in lieu of functions as template alias arguments to 
 various public functions in these modules. The string becomes 
 the return expression of an anonymous function which implicitly 
 has one or two arguments, named "a" and "b", like the string "a 
 < 3" in the following example expression:

   arr.filter!"a < 3"

 When this feature was developed, there were no lambda function 
 literals in the language. There were anonymous functions, but 
 their syntactical overhead means they fare poorly as a 
 replacement for lambda functions:

   arr.filter!((a) { return a < 3; })

 The effect is particularly jarring in bigger compositions:

   assert([ 1, 2, 3, 4, 5 ].filter!((a) { return a < 3; 
 }).equal([1, 2]));

 Since then, a first-class lambda syntax has been introduced, 
 with significantly less syntactic overhead:

   arr.filter!(a => a < 3)

 The issue is then: is the old string lambda syntax obsolete in 
 the face of the new lambda literals?

 ----------

 My opinion on the matter is that the *only* advantage of string 
 lambdas is that they are (even) shorter than the new lambda 
 syntax. However, I don't think that comes even close to making 
 up for its many disadvantages; here are the ones I think are 
 the biggest:

  * The number one reason string lambdas are shorter is because 
 they implicitly introduce their parameters. However, this means 
 that you're always stuck with the generic, uninformative 
 parameter names "a" and "b".
  * To the uninitiated, they may not look like code. They 
 certainly aren't highlighted as D code in your average code 
 editor (the q{} literal deserves a mention, but then some of 
 the succinctness advantage is lost and it's not just for D 
 code), and the magically, implicitly introduced variables "a" 
 and "b" is something you plain just have to know beforehand to 
 understand.
  * Apart from "a" and "b", they can only access symbols that 
 are visible in the scope of the std.functional module.

 In light of the above points, I just find them excessively 
 arcane - something new D programmers shouldn't have to learn 
 and thus none of us should continue to use or promote. The 
 symbols you can access in a string lambda are determined by the 
 implementation details of std.functional and thus, may change 
 at any time. It is then only reasonable to recommend that the 
 programmer should not depend on any symbols except for "a" and 
 "b", severely limiting the utility of string lambdas. Also, 
 they only exist in unary and binary forms at present, which 
 means new functions with different requirements cannot leverage 
 them. And then comes the point about consistency; if the 
 utility of string lambdas is so limited in the face of the 
 general lambda literal syntax, foregoing string lambdas in 
 favour of lambda literals helps code consistency.

 I can also think of some relatively minor disadvantages of 
 string lambdas, such as the compile-time performance cost they 
 incur.

 I think string lambdas were a valiant effort to fill a glaring 
 hole in the language at the time, but are now superseded by an 
 overall superior alternative. The cognitive and maintenance 
 load on programmers is not worth their marginal utility. I 
 think we should deprecate string lambdas altogether.

 Specifically, I suggest the following deprecation path:

  * Add deprecation notes to std.functional's unaryFun and 
 binaryFun so users are dissuaded from using them in new code. 
 In time, we would remove their documentation.
  * Leave support for string lambdas in existing Phobos 
 functions for the foreseeable future, for 
 backwards-compatibility purposes.
  * Change all documentation so that it doesn't mention string 

 (Switch std.algorithm/.range to lambda syntax)[2] attempted 
 this and was approved and merged, but subsequently reverted due 
 to bugs.
  * New functions would not support string lambdas.

 ----------

 To be honest, I thought this was a foregone conclusion, but 
 comments on Github indicate otherwise, hence this thread.

 What does everyone think?

  [1] https://github.com/D-Programming-Language/phobos/pull/1453
  [2] https://github.com/D-Programming-Language/phobos/pull/707
I could not agree more! Naturally the deprecation path should be done carefully, as many people already pointed out.
Aug 07 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
I kind of loved old string lambdas in std.algorithm and was 
against introduction of new shorter lambda syntax. Still not 
convinced by the change but keeping both does not make sense for 
sure. So, yes, string lambdas should be deprecated and normal 
ones patches to support all remaining use cases.
Aug 07 2013
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, August 06, 2013 11:05:56 Jakob Ovrum wrote:
 What does everyone think?
I'm completely opposed to the removal of string lambdas. Obviously, they don't always work. More complicated functions definitely need to use the newer lambda syntax or function literals. But for simple lambdas, they're way better. I'd _much_ rather have func!"a != b"(args) than func!((a, b) => a != b)(args) or func!"a < b"(args) than func!((a, b) => a < b)(args). String lambdas are nice and short, and I think that they're plenty clear, and if they're not, they're trivial to explain. We already have string lambdas. Removing them would break code for little benefit IMHO. If you prefer to use the newer lambda syntax, then use that, but I think that it's a definite loss if we get rid of the string lambdas, as they're so much more concise when they do work. I also think that all new Phobos functions which take unary or binary predicates should support string lambdas. It's trivial for them to do so, and it creates needless inconsistencies if some functions support them and some don't. - Jonathan M Davis
Aug 06 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/6/13, Jakob Ovrum <jakobovrum gmail.com> wrote:
   * Leave support for string lambdas in existing Phobos functions
 for the foreseeable future, for backwards-compatibility purposes.
   * New functions would not support string lambdas.
Yeah, I agree. There's a benefit of having less features that do the exact same thing, especially for people new to D. Btw, I don't buy Jonathan's "less is better" argument at all, especially knowing what std.datetime looks like. He's arguing that string lambdas are somehow smaller, yet he writes code with symbol names such as hasOverloadedOpBinaryWithSelf, hasOverloadedOpAssignWithDuration, tzDatabaseNameToWindowsTZName, and forces us to write code like so: cast(DateTime)SysTime(unixTimeToStdTime(_unixIntTime); or even: if (curTime.peek > cast(TickDuration)1.seconds) { } Where is the short syntax for these expressions? I have to write my own wrappers for these of course. And JMD also argues that "seconds" is somehow better then "secs". "secs" is simple and consistent with "nsecs", "usecs", "msecs", but he always shots it down like it's some kind of abomination because "standards and stuff" which nobody really cares about. I always end up writing "secs" and get back a compiler error, it's frustrating. I'd say keep the current string lambda parameters intact, but don't introduce any new ones to new functions.
Aug 06 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Aug 06, 2013 at 11:05:56AM +0200, Jakob Ovrum wrote:

 string lambda functions has again cropped up. I think we should
 clearly decide on some things regarding them. Questions such as; are
 they a worthwhile alternative in the present language? Should they
 be deprecated? Should they be supported in new additions to Phobos?
 
 Some background: string lambda functions are a feature of
 std.functional/std.range/std.algorithm, where strings can be passed
 in lieu of functions as template alias arguments to various public
 functions in these modules. The string becomes the return expression
 of an anonymous function which implicitly has one or two arguments,
 named "a" and "b", like the string "a < 3" in the following example
 expression:
 
   arr.filter!"a < 3"
 
 When this feature was developed, there were no lambda function
 literals in the language. There were anonymous functions, but their
 syntactical overhead means they fare poorly as a replacement for
 lambda functions:
 
   arr.filter!((a) { return a < 3; })
 
 The effect is particularly jarring in bigger compositions:
 
   assert([ 1, 2, 3, 4, 5 ].filter!((a) { return a < 3; }).equal([1,
 2]));
 
 Since then, a first-class lambda syntax has been introduced, with
 significantly less syntactic overhead:
 
   arr.filter!(a => a < 3)
 
 The issue is then: is the old string lambda syntax obsolete in the
 face of the new lambda literals?
[...] Seems this thread has quietened down. So, what is the conclusion? Seems like almost everyone concedes that silent deprecation is the way to go. We still support string lambdas in the background, but in public docs we promote the use of the new lambda syntax. Would that be a fair assessment of this discussion? What about new Phobos functions? Should we continue to support string lambdas in new code? T -- Life is unfair. Ask too much from it, and it may decide you don't deserve what you have now either.
Aug 08 2013
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, August 08, 2013 07:29:56 H. S. Teoh wrote:
 Seems this thread has quietened down. So, what is the conclusion? Seems
 like almost everyone concedes that silent deprecation is the way to go.
 We still support string lambdas in the background, but in public docs we
 promote the use of the new lambda syntax. Would that be a fair
 assessment of this discussion?
I find it interesting that very few Phobos devs have weighed in on the matter, but unfortunately, most of the posters who have weighed in do seem to be against keeping them.
 What about new Phobos functions? Should we continue to support string
 lambdas in new code?
Personally, I don't think that silent deprecation is ever a good idea. If we do that, we're going to end up with confusion over which functions accept strings and which don't, and it won't be clear that we're trying to get rid of them. I think that we need to clearly choose one path or the other: either 1. Choose to keep string lambdas around permanently and support them, even if they're used for a limited number of use cases (e.g. with simple lambdas which use operators but not function calls). or 2. Put them on the path to deprecation towards removal (in which case, they might actually stay marked as deprecated for quite some time to come but would clearly be unacceptable for new code and might be fully removed at some point in the future). - Jonathan M Davis
Aug 08 2013
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/8/13 9:52 AM, Jonathan M Davis wrote:
 On Thursday, August 08, 2013 07:29:56 H. S. Teoh wrote:
 Seems this thread has quietened down. So, what is the conclusion? Seems
 like almost everyone concedes that silent deprecation is the way to go.
 We still support string lambdas in the background, but in public docs we
 promote the use of the new lambda syntax. Would that be a fair
 assessment of this discussion?
I find it interesting that very few Phobos devs have weighed in on the matter, but unfortunately, most of the posters who have weighed in do seem to be against keeping them.
There's a related issue that I think we must solve before deciding whether or not we should deprecate string lambdas. Consider: void main() { import std.range; SortedRange!(int[], "a > b") a; SortedRange!(int[], "a > b") b; b = a; SortedRange!(int[], (a, b) => a > b) c; SortedRange!(int[], (a, b) => a > b) d; d = c; } The last line fails to compile because D does not currently have a good notion of comparing lambdas for equality. In contrast, string comparison is well defined, and although string lambdas have clowny issues with e.g. "a>b" being different from "a > b", people have a good understanding of what to do to get code working. So I think we should come up with a good definition of what comparing two function aliases means. Andrei
Aug 11 2013
next sibling parent reply "Tyler Jameson Little" <beatgammit gmail.com> writes:
On Sunday, 11 August 2013 at 16:26:16 UTC, Andrei Alexandrescu 
wrote:
 On 8/8/13 9:52 AM, Jonathan M Davis wrote:
 On Thursday, August 08, 2013 07:29:56 H. S. Teoh wrote:
 Seems this thread has quietened down. So, what is the 
 conclusion? Seems
 like almost everyone concedes that silent deprecation is the 
 way to go.
 We still support string lambdas in the background, but in 
 public docs we
 promote the use of the new lambda syntax. Would that be a fair
 assessment of this discussion?
I find it interesting that very few Phobos devs have weighed in on the matter, but unfortunately, most of the posters who have weighed in do seem to be against keeping them.
There's a related issue that I think we must solve before deciding whether or not we should deprecate string lambdas. Consider: void main() { import std.range; SortedRange!(int[], "a > b") a; SortedRange!(int[], "a > b") b; b = a; SortedRange!(int[], (a, b) => a > b) c; SortedRange!(int[], (a, b) => a > b) d; d = c; } The last line fails to compile because D does not currently have a good notion of comparing lambdas for equality. In contrast, string comparison is well defined, and although string lambdas have clowny issues with e.g. "a>b" being different from "a > b", people have a good understanding of what to do to get code working. So I think we should come up with a good definition of what comparing two function aliases means. Andrei
Correct me if I'm wrong, but AFAICT the old behavior was an undocumented feature. I couldn't find string lambdas formally documented anywhere, but lambdas are. Comparing function aliases is an optimization, not a feature, so I don't feel it's a blocker to deprecating string lambdas. If the user needs the old behavior, he/she can do this today with an actual function: bool gt(int a, int b) { return a > b; } void main() { import std.range; SortedRange!(int[], "a > b") a; SortedRange!(int[], "a > b") b; b = a; SortedRange!(int[], gt) c; SortedRange!(int[], gt) d; d = c; } While not as concise, this is safer and does not rely on undocumented behavior. Another consideration, are the following equivalent? (a,b) => a > b (b,c) => b > c
Aug 11 2013
parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Sunday, 11 August 2013 at 18:30:02 UTC, Tyler Jameson Little 
wrote:
 Correct me if I'm wrong, but AFAICT the old behavior was an 
 undocumented feature. I couldn't find string lambdas formally 
 documented anywhere, but lambdas are.
They are documented in std.functional: Namely because they are not a language feature but instead the power of D's other features.
 Comparing function aliases is an optimization, not a feature,
I don't really think it is an optimization either. What Andrei was requesting was a definition of what constitutes equality for equality for lambdas. However none of what I said should be taken to contradict your point that one can still use a named function to keep types equal.
 If the user needs the old behavior, he/she can do this today 
 with an actual function
This assumes you are not receiving a sorted range from a third party library and passing it to another third party.
Aug 13 2013
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 8/11/2013 9:26 AM, Andrei Alexandrescu wrote:
 There's a related issue that I think we must solve before deciding whether or
 not we should deprecate string lambdas. Consider:

 void main() {
      import std.range;
      SortedRange!(int[], "a > b") a;
      SortedRange!(int[], "a > b") b;
      b = a;
      SortedRange!(int[], (a, b) => a > b) c;
      SortedRange!(int[], (a, b) => a > b) d;
      d = c;
 }

 The last line fails to compile because D does not currently have a good notion
 of comparing lambdas for equality.
Bugzilla?
Aug 11 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/11/13 11:46 AM, Walter Bright wrote:
 On 8/11/2013 9:26 AM, Andrei Alexandrescu wrote:
 There's a related issue that I think we must solve before deciding
 whether or
 not we should deprecate string lambdas. Consider:

 void main() {
      import std.range;
      SortedRange!(int[], "a > b") a;
      SortedRange!(int[], "a > b") b;
      b = a;
      SortedRange!(int[], (a, b) => a > b) c;
      SortedRange!(int[], (a, b) => a > b) d;
      d = c;
 }

 The last line fails to compile because D does not currently have a
 good notion
 of comparing lambdas for equality.
Bugzilla?
http://d.puremagic.com/issues/show_bug.cgi?id=10819 Andrei
Aug 13 2013
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sun, Aug 11, 2013 at 09:26:17AM -0700, Andrei Alexandrescu wrote:
 On 8/8/13 9:52 AM, Jonathan M Davis wrote:
On Thursday, August 08, 2013 07:29:56 H. S. Teoh wrote:
Seems this thread has quietened down. So, what is the conclusion?
Seems like almost everyone concedes that silent deprecation is the
way to go.  We still support string lambdas in the background, but
in public docs we promote the use of the new lambda syntax. Would
that be a fair assessment of this discussion?
I find it interesting that very few Phobos devs have weighed in on the matter, but unfortunately, most of the posters who have weighed in do seem to be against keeping them.
There's a related issue that I think we must solve before deciding whether or not we should deprecate string lambdas. Consider: void main() { import std.range; SortedRange!(int[], "a > b") a; SortedRange!(int[], "a > b") b; b = a; SortedRange!(int[], (a, b) => a > b) c; SortedRange!(int[], (a, b) => a > b) d; d = c; } The last line fails to compile because D does not currently have a good notion of comparing lambdas for equality. In contrast, string comparison is well defined, and although string lambdas have clowny issues with e.g. "a>b" being different from "a > b", people have a good understanding of what to do to get code working. So I think we should come up with a good definition of what comparing two function aliases means.
[...] I'm not sure how the compiler handles delegates internally, but a first shot at it might be, any delegate with the same expression tree within the same scope should compare equally. I don't know if this is actually feasible to implement, though (I'm not familiar with DMD code). The reason it must be the same scope is because otherwise, the delegate might be closing over different variables, so it should not be treated the same way (otherwise two delegates that are textwise the same may have different runtime behaviours, leading to a wrong conflation of template instantiations). T -- Elegant or ugly code as well as fine or rude sentences have something in common: they don't depend on the language. -- Luca De Vitis
Aug 13 2013
prev sibling parent "David Nadlinger" <code klickverbot.at> writes:
On Saturday, 10 August 2013 at 18:28:29 UTC, Jonathan M Davis 
wrote:
 I find it interesting that very few Phobos devs have weighed in 
 on the matter,
 but unfortunately, most of the posters who have weighed in do 
 seem to be
 against keeping them.
I am not actively participating in any NG discussions right now due to university work, but for the record, I am very much in favor of phasing out string lambdas as well (even if short-term removal is certainly not possible at this point). I am sure all the relevant arguments have been brought up already, so I am not going to repeat them all over again, but in my opinion, the increased cognitive load for the user (a new syntax to learn) and the fact that string lambdas can't work for any cases involving free functions (std.functional importing half of Phobos just in case a string lambda could need a certain function clearly isn't a scalable solution) are more than enough a reason to ditch them. String lambdas were a brilliant hack back then, but now that we have a proper solution, it's time to let them go. David
Aug 11 2013
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Aug 08, 2013 at 09:52:05AM -0700, Jonathan M Davis wrote:
 On Thursday, August 08, 2013 07:29:56 H. S. Teoh wrote:
 Seems this thread has quietened down. So, what is the conclusion?
 Seems like almost everyone concedes that silent deprecation is the
 way to go.  We still support string lambdas in the background, but
 in public docs we promote the use of the new lambda syntax. Would
 that be a fair assessment of this discussion?
I find it interesting that very few Phobos devs have weighed in on the matter, but unfortunately, most of the posters who have weighed in do seem to be against keeping them.
[...] Well, it would be nice of the rest of the Phobos devs speak up, otherwise they are giving the wrong impression about the state of things. T -- Many open minds should be closed for repairs. -- K5 user
Aug 08 2013
parent Walter Bright <newshound2 digitalmars.com> writes:
On 8/8/2013 10:02 AM, H. S. Teoh wrote:
 Well, it would be nice of the rest of the Phobos devs speak up,
 otherwise they are giving the wrong impression about the state of
 things.
See Andrei's reply.
Aug 11 2013
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On 6 August 2013 19:25, Jonathan M Davis <jmdavisProg gmx.com> wrote:

 On Tuesday, August 06, 2013 11:05:56 Jakob Ovrum wrote:
 What does everyone think?
I'm completely opposed to the removal of string lambdas. Obviously, they don't always work. More complicated functions definitely need to use the newer lambda syntax or function literals. But for simple lambdas, they're way better. I'd _much_ rather have func!"a != b"(args) than func!((a, b) => a != b)(args) or func!"a < b"(args) than func!((a, b) => a < b)(args). String lambdas are nice and short, and I think that they're plenty clear, and if they're not, they're trivial to explain.
Can you give an example where you've actually used a string lambda before where the predicate is more complex than a basic comparison? Surely the solution to this problem is to offer a bunch of templates that perform the most common predicates in place of unary/binaryFun? So rather than: func!((a, b) => a < b)(args) You use: func!binaryLess(args) Or something like that? The thing that annoys me about string vs proper lambda's, is that I never know which one I'm supposed to use. I need to refer to documentation every time. Also, the syntax highlighting fails. We already have string lambdas. Removing them would break code for little
 benefit IMHO. If you prefer to use the newer lambda syntax, then use that,
 but
 I think that it's a definite loss if we get rid of the string lambdas, as
 they're so much more concise when they do work.

 I also think that all new Phobos functions which take unary or binary
 predicates should support string lambdas. It's trivial for them to do so,
 and
 it creates needless inconsistencies if some functions support them and some
 don't.

 - Jonathan M Davis
Aug 13 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 14 August 2013 at 02:05:16 UTC, Manu wrote:
 Can you give an example where you've actually used a string 
 lambda before
 where the predicate is more complex than a basic comparison?
 Surely the solution to this problem is to offer a bunch of 
 templates that
 perform the most common predicates in place of unary/binaryFun?

 So rather than: func!((a, b) => a < b)(args)
 You use: func!binaryLess(args)

 Or something like that?
I was unsure about the solution we should adopt until I read that. This is what we need.
Aug 13 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 14 August 2013 at 02:05:16 UTC, Manu wrote:
 So rather than: func!((a, b) => a < b)(args)
 You use: func!binaryLess(args)

 Or something like that?
I don't really like that solution, but I got a little closer: func!(opBinary!">")(args); The issue here are: func!"a.label < b.label"(args); and unaryFun: filter!"a > 0"(args); maybe: filter!(opUnary!">"(0))(args); Which is problematic: someFun!(opUnary!"++")(args); I find the string lambda to be perfect for the simple logic, and there isn't really a highlighting issue since there usually isn't anything to highlight anyway; and we have q{ tokens here }.
Aug 13 2013
prev sibling parent reply "Brad Anderson" <eco gnuk.net> writes:
On Wednesday, 14 August 2013 at 02:05:16 UTC, Manu wrote:
 Can you give an example where you've actually used a string 
 lambda before
 where the predicate is more complex than a basic comparison?
 Surely the solution to this problem is to offer a bunch of 
 templates that
 perform the most common predicates in place of unary/binaryFun?

 So rather than: func!((a, b) => a < b)(args)
 You use: func!binaryLess(args)

 Or something like that?
How about just "less"? It's what C++ STL uses (std::less, std::greater, std::negate, etc...). In C++, however, you have to do some truly ugly stuff to actually make use of the predefined function objects...bind1st...eww (the new C++11 bind is only marginally better but C++11 does have lambdas now at least).
 The thing that annoys me about string vs proper lambda's, is 
 that I never
 know which one I'm supposed to use. I need to refer to 
 documentation every
 time.
 Also, the syntax highlighting fails.
Aug 13 2013
parent reply "Tyler Jameson Little" <beatgammit gmail.com> writes:
On Wednesday, 14 August 2013 at 05:44:50 UTC, Brad Anderson wrote:
 On Wednesday, 14 August 2013 at 02:05:16 UTC, Manu wrote:
 Can you give an example where you've actually used a string 
 lambda before
 where the predicate is more complex than a basic comparison?
 Surely the solution to this problem is to offer a bunch of 
 templates that
 perform the most common predicates in place of unary/binaryFun?

 So rather than: func!((a, b) => a < b)(args)
 You use: func!binaryLess(args)

 Or something like that?
How about just "less"? It's what C++ STL uses (std::less, std::greater, std::negate, etc...). In C++, however, you have to do some truly ugly stuff to actually make use of the predefined function objects...bind1st...eww (the new C++11 bind is only marginally better but C++11 does have lambdas now at least).
 The thing that annoys me about string vs proper lambda's, is 
 that I never
 know which one I'm supposed to use. I need to refer to 
 documentation every
 time.
 Also, the syntax highlighting fails.
Or imitate bash: Binary: - gt: a > b - ge: a >= b - lt: a < b - le: a <= b - eq: a == b - ne: a != b Unary: - z: (zero) a == 0 (if range, a.empty?) - n: (non-zero) a != 0 Perhaps this is *too* terse?
Aug 14 2013
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 14, 2013 at 09:10:37AM +0200, Tyler Jameson Little wrote:
 On Wednesday, 14 August 2013 at 05:44:50 UTC, Brad Anderson wrote:
On Wednesday, 14 August 2013 at 02:05:16 UTC, Manu wrote:
Can you give an example where you've actually used a string lambda
before where the predicate is more complex than a basic comparison?
Surely the solution to this problem is to offer a bunch of templates
that perform the most common predicates in place of unary/binaryFun?

So rather than: func!((a, b) => a < b)(args)
You use: func!binaryLess(args)

Or something like that?
How about just "less"? It's what C++ STL uses (std::less, std::greater, std::negate, etc...). In C++, however, you have to do some truly ugly stuff to actually make use of the predefined function objects...bind1st...eww (the new C++11 bind is only marginally better but C++11 does have lambdas now at least).
[...]
 Or imitate bash:
 
 Binary:
 - gt: a > b
 - ge: a >= b
 - lt: a < b
 - le: a <= b
 - eq: a == b
 - ne: a != b
 
 Unary:
 - z: (zero) a == 0 (if range, a.empty?)
 - n: (non-zero) a != 0
 
 Perhaps this is *too* terse?
That's a bit too terse. What about this: less // a < b less!(5) // a < 5 lessEq // a <= b lessEq!(5) // a <= 5 more // a > b more!(5) // a > 5 moreEq // a >= b moreEq!(5) // a >= 5 equal // a == b equal!(5) // a == 5 notEqual // a != b notEqual!(5) // a != 5 T -- A computer doesn't mind if its programs are put to purposes that don't match their names. -- D. Knuth
Aug 14 2013
next sibling parent "Wyatt" <wyatt.epp gmail.com> writes:
On Wednesday, 14 August 2013 at 14:36:25 UTC, H. S. Teoh wrote:
 	more		// a > b
 	more!(5)	// a > 5
 	moreEq		// a >= b
 	moreEq!(5)	// a >= 5
Nitpick, but I'd personally prefer "greater" rather than "more". -Wyatt
Aug 14 2013
prev sibling next sibling parent reply "Mr. Anonymous" <mailnew4ster gmail.com> writes:
On Wednesday, 14 August 2013 at 14:36:25 UTC, H. S. Teoh wrote:
 A computer doesn't mind if its programs are put to purposes 
 that don't match their names. -- D. Knuth
Heh :) Sometimes I read your replies just for the signature. On the topic, I think the variant with the operators is more straightforward, e.g.:
 	less		// a < b
op!"<"
 	less!(5)	// a < 5
op!"<"(5) etc.
Aug 14 2013
parent Manu <turkeyman gmail.com> writes:
I like the direction this thread is going :)


On 15 August 2013 00:50, Mr. Anonymous <mailnew4ster gmail.com> wrote:

 On Wednesday, 14 August 2013 at 14:36:25 UTC, H. S. Teoh wrote:

 A computer doesn't mind if its programs are put to purposes that don't
 match their names. -- D. Knuth
Heh :) Sometimes I read your replies just for the signature. On the topic, I think the variant with the operators is more straightforward, e.g.:
         less            // a < b
op!"<"
         less!(5)        // a < 5
op!"<"(5) etc.
Aug 14 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/14/13 7:34 AM, H. S. Teoh wrote:
 On Wed, Aug 14, 2013 at 09:10:37AM +0200, Tyler Jameson Little wrote:
 On Wednesday, 14 August 2013 at 05:44:50 UTC, Brad Anderson wrote:
 On Wednesday, 14 August 2013 at 02:05:16 UTC, Manu wrote:
 Can you give an example where you've actually used a string lambda
 before where the predicate is more complex than a basic comparison?
 Surely the solution to this problem is to offer a bunch of templates
 that perform the most common predicates in place of unary/binaryFun?

 So rather than: func!((a, b) => a < b)(args)
 You use: func!binaryLess(args)

 Or something like that?
How about just "less"? It's what C++ STL uses (std::less, std::greater, std::negate, etc...). In C++, however, you have to do some truly ugly stuff to actually make use of the predefined function objects...bind1st...eww (the new C++11 bind is only marginally better but C++11 does have lambdas now at least).
[...]
 Or imitate bash:

 Binary:
 - gt: a > b
 - ge: a >= b
 - lt: a < b
 - le: a <= b
 - eq: a == b
 - ne: a != b

 Unary:
 - z: (zero) a == 0 (if range, a.empty?)
 - n: (non-zero) a != 0

 Perhaps this is *too* terse?
That's a bit too terse. What about this: less // a < b less!(5) // a < 5 lessEq // a <= b lessEq!(5) // a <= 5 more // a > b more!(5) // a > 5 moreEq // a >= b moreEq!(5) // a >= 5 equal // a == b equal!(5) // a == 5 notEqual // a != b notEqual!(5) // a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive. Andrei
Aug 14 2013
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu wrote:
 On 8/14/13 7:34 AM, H. S. Teoh wrote:
[...]
That's a bit too terse. What about this:

	less		// a < b
	less!(5)	// a < 5
	lessEq		// a <= b
	lessEq!(5)	// a <= 5
	more		// a > b
	more!(5)	// a > 5
	moreEq		// a >= b
	moreEq!(5)	// a >= 5
	equal		// a == b
	equal!(5)	// a == 5
	notEqual	// a != b
	notEqual!(5)	// a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
[...] It just occurred to me, that perhaps what we really need here is an even more abbreviated form of lambda literals, like this: sort!(a < b)(range); where 'a' and 'b' are undefined identifiers in the current scope, and the compiler would know to bind them to lambda parameters. Defined identifiers would, naturally, bind to whatever they refer to: int x = 5; find!(a == x)(range); This would be equivalent to: int x = 5; find!((a) => a==x)(range); IOW, an expression that references undefined identifiers in a template parameter would be turned into a lambda that parametrize said identifiers. T -- Beware of bugs in the above code; I have only proved it correct, not tried it. -- Donald Knuth
Aug 14 2013
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 14 August 2013 at 16:38:57 UTC, H. S. Teoh wrote:
 On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu 
 wrote:
 On 8/14/13 7:34 AM, H. S. Teoh wrote:
[...]
That's a bit too terse. What about this:

	less		// a < b
	less!(5)	// a < 5
	lessEq		// a <= b
	lessEq!(5)	// a <= 5
	more		// a > b
	more!(5)	// a > 5
	moreEq		// a >= b
	moreEq!(5)	// a >= 5
	equal		// a == b
	equal!(5)	// a == 5
	notEqual	// a != b
	notEqual!(5)	// a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
[...] It just occurred to me, that perhaps what we really need here is an even more abbreviated form of lambda literals, like this: sort!(a < b)(range); where 'a' and 'b' are undefined identifiers in the current scope, and the compiler would know to bind them to lambda parameters. Defined identifiers would, naturally, bind to whatever they refer to: int x = 5; find!(a == x)(range); This would be equivalent to: int x = 5; find!((a) => a==x)(range); IOW, an expression that references undefined identifiers in a template parameter would be turned into a lambda that parametrize said identifiers. T
That's a bit too fragile IMO. int b; //rename to 'a' // lots of intermediate code... int x = 5; r.blah!(a == x); Renaming of a seemingly unrelated variable would result in spooky action-at-a-distance behaviour.
Aug 14 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 14, 2013 at 07:03:32PM +0200, John Colvin wrote:
 On Wednesday, 14 August 2013 at 16:38:57 UTC, H. S. Teoh wrote:
On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu
wrote:
On 8/14/13 7:34 AM, H. S. Teoh wrote:
[...]
That's a bit too terse. What about this:

	less		// a < b
	less!(5)	// a < 5
	lessEq		// a <= b
	lessEq!(5)	// a <= 5
	more		// a > b
	more!(5)	// a > 5
	moreEq		// a >= b
	moreEq!(5)	// a >= 5
	equal		// a == b
	equal!(5)	// a == 5
	notEqual	// a != b
	notEqual!(5)	// a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
[...] It just occurred to me, that perhaps what we really need here is an even more abbreviated form of lambda literals, like this: sort!(a < b)(range); where 'a' and 'b' are undefined identifiers in the current scope, and the compiler would know to bind them to lambda parameters. Defined identifiers would, naturally, bind to whatever they refer to: int x = 5; find!(a == x)(range); This would be equivalent to: int x = 5; find!((a) => a==x)(range); IOW, an expression that references undefined identifiers in a template parameter would be turned into a lambda that parametrize said identifiers. T
That's a bit too fragile IMO. int b; //rename to 'a' // lots of intermediate code... int x = 5; r.blah!(a == x); Renaming of a seemingly unrelated variable would result in spooky action-at-a-distance behaviour.
Arguably, naming a variable as 'b' or 'a' is a bad idea, but, point taken. I guess we still have to fall back to full-fledged lambda literals, then. I'm still in favor of deprecating string lambdas, but as Andrei said, we need to address the problem of how to compare lambdas meaningfully. The question then becomes, what do we do *now*, for new additions to Phobos? Should new code still support string lambdas or not? T -- Right now I'm having amnesia and deja vu at the same time. I think I've forgotten this before.
Aug 14 2013
prev sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
14-Aug-2013 20:37, H. S. Teoh пишет:
 On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu wrote:
[snip]
 It just occurred to me, that perhaps what we really need here is an
 even more abbreviated form of lambda literals, like this:

 	sort!(a < b)(range);

 where 'a' and 'b' are undefined identifiers in the current scope, and
 the compiler would know to bind them to lambda parameters. Defined
 identifiers would, naturally, bind to whatever they refer to:
Make that sort!( _ < _)(range) and you have some Scala :)
 	int x = 5;
 	find!(a == x)(range);

 This would be equivalent to:

 	int x = 5;
 	find!((a) => a==x)(range);

 IOW, an expression that references undefined identifiers in a template
 parameter would be turned into a lambda that parametrize said
 identifiers.


 T
-- Dmitry Olshansky
Aug 14 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/14/13 10:15 AM, Dmitry Olshansky wrote:
 14-Aug-2013 20:37, H. S. Teoh пишет:
 On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu wrote:
[snip]
 It just occurred to me, that perhaps what we really need here is an
 even more abbreviated form of lambda literals, like this:

     sort!(a < b)(range);

 where 'a' and 'b' are undefined identifiers in the current scope, and
 the compiler would know to bind them to lambda parameters. Defined
 identifiers would, naturally, bind to whatever they refer to:
Make that sort!( _ < _)(range) and you have some Scala :)
Really? How does Scala figure that _ < _ refers to two arguments and not one? Andrei
Aug 14 2013
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
14-Aug-2013 21:17, Andrei Alexandrescu пишет:
 On 8/14/13 10:15 AM, Dmitry Olshansky wrote:
 14-Aug-2013 20:37, H. S. Teoh пишет:
 On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu wrote:
[snip]
 It just occurred to me, that perhaps what we really need here is an
 even more abbreviated form of lambda literals, like this:

     sort!(a < b)(range);

 where 'a' and 'b' are undefined identifiers in the current scope, and
 the compiler would know to bind them to lambda parameters. Defined
 identifiers would, naturally, bind to whatever they refer to:
Make that sort!( _ < _ )(range) and you have some Scala :)
Really? How does Scala figure that _ < _ refers to two arguments and not one?
Looking into "Programming in Scala, 2nd edition" again... Left to right, each underscore is a new parameter. Once you need the argument 2 times you are screwed and go back to the same syntax we have. Even more funny is that Scala can't bind templated parameters... val f = _ + _ Not going to fly since there is no context to infer argument types! Hence the crufty: val f = ( _ : Int) + ( _ : Int) In D: alias f = _ + _; Could work since with a bit of compiler magic (expression alias). Or even alias f = typeof(_ + _); with expression templates. Sounds like a solution but that would destroy the easy ordering of arguments...
 Andrei
-- Dmitry Olshansky
Aug 14 2013
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Wednesday, 14 August 2013 at 18:18:32 UTC, Dmitry Olshansky 
wrote:
 Could work since with a bit of compiler magic (expression 
 alias).
 Or even
 alias f = typeof(_ + _);
 with expression templates. Sounds like a solution but that 
 would destroy the easy ordering of arguments...
And here one should probably stop for a while and ask: "is it worth it?"
Aug 14 2013
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 8/14/2013 11:21 AM, Dicebot wrote:
 On Wednesday, 14 August 2013 at 18:18:32 UTC, Dmitry Olshansky wrote:
 Could work since with a bit of compiler magic (expression alias).
 Or even
 alias f = typeof(_ + _);
 with expression templates. Sounds like a solution but that would destroy the
 easy ordering of arguments...
And here one should probably stop for a while and ask: "is it worth it?"
+1 for common sense.
Aug 14 2013
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
14-Aug-2013 22:21, Dicebot пишет:
 On Wednesday, 14 August 2013 at 18:18:32 UTC, Dmitry Olshansky wrote:
 Could work since with a bit of compiler magic (expression alias).
 Or even
 alias f = typeof(_ + _);
 with expression templates. Sounds like a solution but that would
 destroy the easy ordering of arguments...
And here one should probably stop for a while and ask: "is it worth it?"
As a library - of course, it's a great exercise at futility. C++ Boost lambda was full of it as they had less powerful language. -- Dmitry Olshansky
Aug 14 2013
prev sibling parent reply "w0rp" <devw0rp gmail.com> writes:
On Wednesday, 14 August 2013 at 18:18:32 UTC, Dmitry Olshansky 
wrote:
 In D:
 alias f = _ + _;
In addition to being a D fan, I am also a Scala fan, and I use the magic underscore syntax in Scala now and again. While it can sometimes be useful, I don't feel like it absolutely needs to exist in the language. I would like to argue in favour of gradually replacing the string lambdas which just lambdas. I don't personally see a huge problem in typing (x, y) => x < y. It very clearly expresses your intent, and this is very brief syntax. The underscore syntax only takes away from the expressiveness to reduce line length. As mentioned before, for very common lambdas that you don't want to type yourself, you could define those functions as generic functions with a given name, like a 'less' function.
Aug 14 2013
parent "Brian Rogoff" <brogoff gmail.com> writes:
On Wednesday, 14 August 2013 at 19:24:43 UTC, w0rp wrote:
 As mentioned before, for very common lambdas that you don't 
 want to type yourself, you could define those functions as 
 generic functions with a given name, like a 'less' function.
Also as mentioned before, the issue is that (anonymous) lambda functions have some subtle issues in regards to templates, because lambda functions cannot be compared for equality (the problem in general is undecidable) while string lambdas can, even though they have the same problem as with some of the unwieldy attempts at comparison (AST comparison after normalization and renaming with De Bruijn indices, or whatever), namely that things that are obviously equal aren't seen as such. That's why you want to name functions, so that templates instantiated with the same function are interoperable. It seems to me that the easiest solution for D would be to deprecate string lambdas, and declare that anonymous lambdas always compare to false. Is the workaround of naming lambdas being used in in separate template instantiations that need to work together so bad?
Aug 14 2013
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-08-14 19:17, Andrei Alexandrescu wrote:

 Really? How does Scala figure that _ < _ refers to two arguments and not
 one?
In Scala _ is something like a wildcard. * It can be used in imports: "import foo.bar._". Means import everything in foo.bar * It can be use as a fallback in pattern matching, like the default in a switch * It can be used in lambdas, as described above -- /Jacob Carlborg
Aug 14 2013
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, August 14, 2013 09:26:20 Andrei Alexandrescu wrote:
 On 8/14/13 7:34 AM, H. S. Teoh wrote:
 That's a bit too terse. What about this:
 less // a < b
 less!(5) // a < 5
 lessEq // a <= b
 lessEq!(5) // a <= 5
 more // a > b
 more!(5) // a > 5
 moreEq // a >= b
 moreEq!(5) // a >= 5
 equal // a == b
 equal!(5) // a == 5
 notEqual // a != b
 notEqual!(5) // a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
I'd simply argue for doing something like binaryOp!"<" and unaryOp!"-". Creating different names for all of the various operators is not at all in line with how do things normally and definitely seems unattractive. In contrast, by creating specific templates for operators, we cover the main use cases where the string lambdas are more attractive than the newer lambda literals. It's also in line with how do operator overloading. - Jonathan M Davis
Aug 14 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 15 August 2013 at 02:30:50 UTC, Jonathan M Davis 
wrote:
 On Wednesday, August 14, 2013 09:26:20 Andrei Alexandrescu 
 wrote:
 On 8/14/13 7:34 AM, H. S. Teoh wrote:
 That's a bit too terse. What about this:
 less // a < b
 less!(5) // a < 5
 lessEq // a <= b
 lessEq!(5) // a <= 5
 more // a > b
 more!(5) // a > 5
 moreEq // a >= b
 moreEq!(5) // a >= 5
 equal // a == b
 equal!(5) // a == 5
 notEqual // a != b
 notEqual!(5) // a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
I'd simply argue for doing something like binaryOp!"<" and unaryOp!"-". Creating different names for all of the various operators is not at all in line with how do things normally and definitely seems unattractive. In contrast, by creating specific templates for operators, we cover the main use cases where the string lambdas are more attractive than the newer lambda literals. It's also in line with how do operator overloading. - Jonathan M Davis
Yes and avoid stupid template duplication like with "a>b" "a > b" or by not having equals delegate literals. This is clearly the best option for simple operations. Complex operation should be migrated to delegate literals.
Aug 14 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Aug 15, 2013 at 07:13:18AM +0200, deadalnix wrote:
 On Thursday, 15 August 2013 at 02:30:50 UTC, Jonathan M Davis wrote:
On Wednesday, August 14, 2013 09:26:20 Andrei Alexandrescu wrote:
On 8/14/13 7:34 AM, H. S. Teoh wrote:
 That's a bit too terse. What about this:
 less // a < b
 less!(5) // a < 5
 lessEq // a <= b
 lessEq!(5) // a <= 5
 more // a > b
 more!(5) // a > 5
 moreEq // a >= b
 moreEq!(5) // a >= 5
 equal // a == b
 equal!(5) // a == 5
 notEqual // a != b
 notEqual!(5) // a != 5
At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
I'd simply argue for doing something like binaryOp!"<" and unaryOp!"-". Creating different names for all of the various operators is not at all in line with how do things normally and definitely seems unattractive. In contrast, by creating specific templates for operators, we cover the main use cases where the string lambdas are more attractive than the newer lambda literals. It's also in line with how do operator overloading.
That's a good idea: unaryOp!"-" // -a binaryOp!"-" // a-b binaryOp!("-", 5) // a-5 binaryOp!"<" // a<b binaryOp!("<", 5) // a<5 [...]
 Yes and avoid stupid template duplication like with "a>b" "a > b" or
 by not having equals delegate literals.
+1
 This is clearly the best option for simple operations. Complex
 operation should be migrated to delegate literals.
+1. T -- "Hi." "'Lo."
Aug 15 2013
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, August 14, 2013 12:05:01 Manu wrote:
 Can you give an example where you've actually used a string lambda before
 where the predicate is more complex than a basic comparison?
You can use string lambdas with anything which std.functional imports, so std.algorithm std.conv std.exception std.math std.range std.string std.traits std.typecons std.typetuple And to show how broken string lambdas are, most of the imports are list like this // for making various functions visible in *naryFun import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; All of those modules are imported specifically so that string lambdas can use their stuff. I'm sure that I've used std.conv.to and various string functions at minimum, since I generally prefer to use string lambdas when I can, but you generally have to just trying them and see what does and doesn't work if you want to use string lambdas for anything besides operators. Operators are where the major gain is though.
 Surely the solution to this problem is to offer a bunch of templates that
 perform the most common predicates in place of unary/binaryFun?
 
 So rather than: func!((a, b) => a < b)(args)
 You use: func!binaryLess(args)
 
 Or something like that?
That would result in too many stray templates. Something like binaryOp!"<" would make more sense. Then we pretty much just need unaryOp and binaryOp rather than a template for every operator. Other than that, it's a good idea though, since it allows us to make the common case cleaner.
 The thing that annoys me about string vs proper lambda's, is that I never
 know which one I'm supposed to use. I need to refer to documentation every
 time.
Unless you happen to have memorized what std.functional imports, it's a guessing game, which is the major problem with string lambdas. It also has the unfortunate side effect of making it so that we can't change what std.functional imports.
 Also, the syntax highlighting fails.
I've never really viewed that as problem. I _like_ the fact that they're highlighted as a string and therefore clearly separate from the rest of the expression. Having syntax highlighting inside the lambda is generally a negative IMHO, though I can see why you'd want it. The only places where I might want it would be complicated enough to require regular lambdas anyway, in which case, it might even be cleaner to use a nested function. - Jonathan M Davis
Aug 13 2013