digitalmars.D - Future of string lambda functions/string predicate functions
- Jakob Ovrum (83/83) Aug 06 2013 In Phobos pull request #1453 (Implement chunkBy.)[1], the topic
- qznc (5/12) Aug 06 2013 I was certainly irritated, when I used the std.functional stuff
- bearophile (11/12) Aug 06 2013 In this page:
- Jakob Ovrum (4/11) Aug 06 2013 I think those few situations should forego string lambdas in
- Jakob Ovrum (7/9) Aug 06 2013 To clarify: those are the benefits we gain under the presumption
- dennis luehring (4/13) Aug 06 2013 100% correct - string lambdas behave just not very generic, at all
- Jakob Ovrum (3/5) Aug 06 2013 It's a "big disadvantage" only if you're following the Perl
- dennis luehring (5/10) Aug 06 2013 according to
- Brad Anderson (3/16) Aug 06 2013 More details from it being reopened:
- Jonathan M Davis (5/16) Aug 06 2013 So, don't use them with functions. Just use them with operators. They're...
- bearophile (21/25) Aug 06 2013 In Scala they sometimes use lambdas where arguments are
- Jakob Ovrum (31/49) Aug 06 2013 I recognize the existence of code where symbol names like "a" and
- H. S. Teoh (45/60) Aug 06 2013 In this case, I'll have to be honest and say that the string lambda is
- Wyatt (8/9) Aug 06 2013 Curse you, flush-to-zero denormal mode! ;)
- Andre Artus (2/2) Aug 06 2013 I would vote for marking it as deprecated, and clearly
- Meta (12/15) Aug 06 2013 Looks good except for the above point. UnaryFun and binaryFun
- Jakob Ovrum (6/17) Aug 06 2013 This is a good point. I also agree that a fully generic, n-ary
- Timon Gehr (2/22) Aug 07 2013
- Jakob Ovrum (5/6) Aug 07 2013 I'd be very happy with such a change, but to my understanding,
- Peter Alexander (7/20) Aug 06 2013 Yes x 4. I think this is the perfect path to their
- John Colvin (2/23) Aug 06 2013 I agree this is the best decision.
- monarch_dodra (45/71) Aug 07 2013 I actual think this is a very bad situation. This puts them in
- Peter Alexander (8/19) Aug 07 2013 String lambdas won't be documented anywhere, so any newcomer to D
- Jakob Ovrum (10/26) Aug 07 2013 Ew. I don't see this as a good argument. Code and strings are
- Jakob Ovrum (6/13) Aug 07 2013 Oops, accidentally reversed it there. Correction:
- Dejan Lekic (3/90) Aug 07 2013 I could not agree more! Naturally the deprecation path should be
- Dicebot (5/5) Aug 07 2013 I kind of loved old string lambdas in std.algorithm and was
- Jonathan M Davis (17/18) Aug 06 2013 I'm completely opposed to the removal of string lambdas. Obviously, they...
- Andrej Mitrovic (21/24) Aug 06 2013 Yeah, I agree. There's a benefit of having less features that do the
- H. S. Teoh (12/47) Aug 08 2013 [...]
- Jonathan M Davis (17/24) Aug 08 2013 I find it interesting that very few Phobos devs have weighed in on the m...
- Andrei Alexandrescu (20/29) Aug 11 2013 There's a related issue that I think we must solve before deciding
- Tyler Jameson Little (26/63) Aug 11 2013 Correct me if I'm wrong, but AFAICT the old behavior was an
- Jesse Phillips (13/19) Aug 13 2013 They are documented in std.functional:
- Walter Bright (2/15) Aug 11 2013 Bugzilla?
- Andrei Alexandrescu (3/22) Aug 13 2013 http://d.puremagic.com/issues/show_bug.cgi?id=10819
- H. S. Teoh (14/47) Aug 13 2013 [...]
- David Nadlinger (17/22) Aug 11 2013 I am not actively participating in any NG discussions right now
- H. S. Teoh (8/18) Aug 08 2013 [...]
- Walter Bright (2/5) Aug 11 2013 See Andrei's reply.
- Manu (13/38) Aug 13 2013 Can you give an example where you've actually used a string lambda befor...
- deadalnix (3/12) Aug 13 2013 I was unsure about the solution we should adopt until I read
- Jesse Phillips (12/15) Aug 13 2013 I don't really like that solution, but I got a little closer:
- Brad Anderson (6/21) Aug 13 2013 How about just "less"? It's what C++ STL uses (std::less,
- Tyler Jameson Little (13/38) Aug 14 2013 Or imitate bash:
- H. S. Teoh (18/51) Aug 14 2013 That's a bit too terse. What about this:
- Wyatt (3/7) Aug 14 2013 Nitpick, but I'd personally prefer "greater" rather than "more".
- Mr. Anonymous (8/12) Aug 14 2013 Heh :)
- Manu (2/17) Aug 14 2013
- Andrei Alexandrescu (4/52) Aug 14 2013 At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes
- H. S. Teoh (20/38) Aug 14 2013 [...]
- John Colvin (8/49) Aug 14 2013 That's a bit too fragile IMO.
- H. S. Teoh (12/69) Aug 14 2013 Arguably, naming a variable as 'b' or 'a' is a bad idea, but, point
- Dmitry Olshansky (7/23) Aug 14 2013 Make that
- Andrei Alexandrescu (4/19) Aug 14 2013 Really? How does Scala figure that _ < _ refers to two arguments and not...
- Dmitry Olshansky (19/40) Aug 14 2013 Looking into "Programming in Scala, 2nd edition" again...
- Dicebot (4/10) Aug 14 2013 And here one should probably stop for a while and ask: "is it
- Walter Bright (2/9) Aug 14 2013 +1 for common sense.
- Dmitry Olshansky (5/12) Aug 14 2013 As a library - of course, it's a great exercise at futility. C++ Boost
- w0rp (14/16) Aug 14 2013 In addition to being a D fan, I am also a Scala fan, and I use
- Brian Rogoff (16/19) Aug 14 2013 Also as mentioned before, the issue is that (anonymous) lambda
- Jacob Carlborg (9/11) Aug 14 2013 In Scala _ is something like a wildcard.
- Jonathan M Davis (8/25) Aug 14 2013 I'd simply argue for doing something like binaryOp!"<" and unaryOp!"-".
- deadalnix (6/38) Aug 14 2013 Yes and avoid stupid template duplication like with "a>b" "a > b"
- H. S. Teoh (13/44) Aug 15 2013 That's a good idea:
- Jonathan M Davis (37/50) Aug 13 2013 You can use string lambdas with anything which std.functional imports, s...
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
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
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
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
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
Am 06.08.2013 12:09, schrieb Jakob Ovrum:On Tuesday, 6 August 2013 at 10:05:54 UTC, Jakob Ovrum wrote: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 :)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
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
Am 06.08.2013 12:49, schrieb Jakob Ovrum:On Tuesday, 6 August 2013 at 10:22:53 UTC, dennis luehring wrote: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 statebut 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
On Tuesday, 6 August 2013 at 10:52:06 UTC, dennis luehring wrote:Am 06.08.2013 12:49, schrieb Jakob Ovrum:More details from it being reopened: https://github.com/D-Programming-Language/phobos/pull/744On Tuesday, 6 August 2013 at 10:22:53 UTC, dennis luehring wrote: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 statebut 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
On Tuesday, August 06, 2013 12:09:25 Jakob Ovrum wrote:On Tuesday, 6 August 2013 at 10:05:54 UTC, Jakob Ovrum wrote: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 DavisI 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
Jakob Ovrum: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, bearophileMy 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
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{aI'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.b}".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
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 case, I'll have to be honest and say that the string lambda is much more readable.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);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 SystemMy 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
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
I would vote for marking it as deprecated, and clearly indicating it as such in the docs.
Aug 06 2013
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
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
On 08/07/2013 06:18 AM, Jakob Ovrum wrote:On Tuesday, 6 August 2013 at 19:38:08 UTC, Meta wrote:I think the way to go for these cases is to fix the parser.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 07 2013
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
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
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:I agree this is the best decision.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
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: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.On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:I agree this is the best decision.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 07 2013
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
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)); //FailsEw. 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
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
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/707I could not agree more! Naturally the deprecation path should be done carefully, as many people already pointed out.
Aug 07 2013
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
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
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
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
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
On 8/8/13 9:52 AM, Jonathan M Davis wrote:On Thursday, August 08, 2013 07:29:56 H. S. Teoh 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. 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. AndreiSeems 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.
Aug 11 2013
On Sunday, 11 August 2013 at 16:26:16 UTC, Andrei Alexandrescu wrote:On 8/8/13 9:52 AM, Jonathan M Davis 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. 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 > cOn Thursday, August 08, 2013 07:29:56 H. S. Teoh 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. 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. AndreiSeems 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.
Aug 11 2013
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 functionThis assumes you are not receiving a sorted range from a third party library and passing it to another third party.
Aug 13 2013
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
On 8/11/13 11:46 AM, Walter Bright wrote:On 8/11/2013 9:26 AM, Andrei Alexandrescu wrote:http://d.puremagic.com/issues/show_bug.cgi?id=10819 AndreiThere'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 13 2013
On Sun, Aug 11, 2013 at 09:26:17AM -0700, Andrei Alexandrescu wrote:On 8/8/13 9:52 AM, Jonathan M Davis wrote:[...] 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 VitisOn Thursday, August 08, 2013 07:29:56 H. S. Teoh 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. 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.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.
Aug 13 2013
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
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:[...] 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 userSeems 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.
Aug 08 2013
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
On 6 August 2013 19:25, Jonathan M Davis <jmdavisProg gmx.com> wrote:On Tuesday, August 06, 2013 11:05:56 Jakob Ovrum 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? 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 littleWhat 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.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
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
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
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
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: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?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 14 2013
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
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 >= 5Nitpick, but I'd personally prefer "greater" rather than "more". -Wyatt
Aug 14 2013
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. KnuthHeh :) 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 < bop!"<"less!(5) // a < 5op!"<"(5) etc.
Aug 14 2013
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. KnuthHeh :) 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 < bop!"<"less!(5) // a < 5op!"<"(5) etc.
Aug 14 2013
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:At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive. AndreiOn 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
Aug 14 2013
On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu wrote:On 8/14/13 7:34 AM, H. S. Teoh wrote:[...][...] 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 KnuthThat'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 != 5At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
Aug 14 2013
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: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.On 8/14/13 7:34 AM, H. S. Teoh wrote:[...][...] 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. TThat'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 != 5At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
Aug 14 2013
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: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.On Wed, Aug 14, 2013 at 09:26:20AM -0700, Andrei Alexandrescu wrote: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.On 8/14/13 7:34 AM, H. S. Teoh wrote:[...][...] 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. TThat'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 != 5At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
Aug 14 2013
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
On 8/14/13 10:15 AM, Dmitry Olshansky wrote:14-Aug-2013 20:37, H. S. Teoh пишет:Really? How does Scala figure that _ < _ refers to two arguments and not one? AndreiOn 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 :)
Aug 14 2013
14-Aug-2013 21:17, Andrei Alexandrescu пишет:On 8/14/13 10:15 AM, Dmitry Olshansky wrote: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...14-Aug-2013 20:37, H. S. Teoh пишет:Really? How does Scala figure that _ < _ refers to two arguments and not one?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 :)Andrei-- Dmitry Olshansky
Aug 14 2013
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
On 8/14/2013 11:21 AM, Dicebot wrote:On Wednesday, 14 August 2013 at 18:18:32 UTC, Dmitry Olshansky wrote:+1 for common sense.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
14-Aug-2013 22:21, Dicebot пишет:On Wednesday, 14 August 2013 at 18:18:32 UTC, Dmitry Olshansky wrote: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 OlshanskyCould 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
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
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
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
On Wednesday, August 14, 2013 09:26:20 Andrei Alexandrescu wrote:On 8/14/13 7:34 AM, H. S. Teoh wrote: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 DavisThat'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 != 5At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
Aug 14 2013
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: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.On 8/14/13 7:34 AM, H. S. Teoh wrote: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 DavisThat'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 != 5At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.
Aug 14 2013
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:That's a good idea: unaryOp!"-" // -a binaryOp!"-" // a-b binaryOp!("-", 5) // a-5 binaryOp!"<" // a<b binaryOp!("<", 5) // a<5 [...]On Wednesday, August 14, 2013 09:26:20 Andrei Alexandrescu wrote:On 8/14/13 7:34 AM, H. S. Teoh wrote: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 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 != 5At this point using "a < b" for a < b, "a < 5" for a < 5 etc. becomes awfully attractive.Yes and avoid stupid template duplication like with "a>b" "a > b" or by not having equals delegate literals.+1This is clearly the best option for simple operations. Complex operation should be migrated to delegate literals.+1. T -- "Hi." "'Lo."
Aug 15 2013
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