August 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"
August 07, 2013
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`.

August 07, 2013
On Tuesday, 6 August 2013 at 09:05:57 UTC, Jakob Ovrum wrote:
> In Phobos pull request #1453 (Implement chunkBy.)[1], the topic 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 lambdas, whether in prose or code. Phobos pull request #707 (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.
August 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.
August 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.
August 10, 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
August 10, 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:
> > 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
August 10, 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.
August 10, 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 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
August 10, 2013
On Tue, Aug 06, 2013 at 11:05:56AM +0200, Jakob Ovrum wrote:
> In Phobos pull request #1453 (Implement chunkBy.)[1], the topic 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?
[...]

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.