August 06, 2013
On Tuesday, 6 August 2013 at 10:52:06 UTC, dennis luehring wrote:
> Am 06.08.2013 12:49, schrieb Jakob Ovrum:
>> On Tuesday, 6 August 2013 at 10:22:53 UTC, dennis luehring wrote:
>>> but normal lambdas do, with the big disadvantage of a little
>>> bit more keyboard grinding :)
>>
>> It's a "big disadvantage" only if you're following the Perl
>> school of programming :P
>>
>
> according to
>
> https://github.com/D-Programming-Language/phobos/pull/707
>
> there where errors and everything was just reverted (without further analysis?) - any idea about the current state

More details from it being reopened: https://github.com/D-Programming-Language/phobos/pull/744
August 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
}
August 06, 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 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.

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.
August 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:
>> 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.
>
> Yes x 4. I think this is the perfect path to their semi-deprecation.
>
> Deprecating them completely, I think, would be unwise since there's a lot of code out there using them. Deprecation through obscurity while retaining backwards-compatibility is the right choice.

I agree this is the best decision.
August 07, 2013
Jakob Ovrum:

>> My opinion is that lambdas are more clean, and I usually prefer them, but in certain situations I prefer string lambdas.
>
> I think those few situations should forego string lambdas in favour of reduced cognitive load and increased consistency.

In Scala they sometimes use lambdas where arguments are underscores (http://stackoverflow.com/questions/7673545/usage-of-in-scala-lambda-functions ).

It's not good to give names to lambda arguments every time. In a program it's useful to give names to variables to make them more readable, but if you look at Haskell code you see several variable names like "a", "b", etc. They use such variable names not because they are lazy people, but because you are writing a very generic function, and very short names are good to remind you they are not important for their semantics, but for other things, like their position.

Using default names as 'a' and 'b' for very simple lambdas is nice. And "(a, b) => a > b)" doesn't give more information than "q{a > b}".

In functional-style code it's important to have means, when you desire it, to write compact code.

So I'd like to keep the string lambdas.

Bye,
bearophile
August 07, 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`.
August 07, 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{a 
> > b}".

I'm not so sure. The lambda literal syntax is a universally applicable language construct that most D programmers will come to know; I don't know if I can say the same about string lambdas. q{}-style string literals are not just for D source code, so syntax highlighting may or may not treat it as such.

With the lambda literal syntax, the names "a" and "b" are not part of any implicit context and have no exceptional meaning - with a few notable exceptions such as the implicit `this` parameter, explicitly introduced symbols are the norm in D.

In some languages, such as Go, one can see a reminiscent style of OOP where even the `this` parameter is explicit. I'm not familiar with the full reasoning behind this so it may be a complete red herring, but I think it's interesting to note.

> In functional-style code it's important to have means, when you desire it, to write compact code.

I completely agree, but I argue that string lambdas enable you to compact your code by means of obscurity, which is counter-productive, assuming the goal is to write more readable code.

August 07, 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:
>> 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 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.
>>
>> Yes x 4. I think this is the perfect path to their semi-deprecation.
>>
>> Deprecating them completely, I think, would be unwise since there's a lot of code out there using them. Deprecation through obscurity while retaining backwards-compatibility is the right choice.
>
> I agree this is the best decision.

I actual think this is a very bad situation. This puts them in the odd spot of "not deprecated, but sometimes supported". This puts the end coder who *wants* to use lambdas (after all they aren't deprecated), in a weird position of "which functions support it, which functions don't?"

Since the end coder is not a D guru, he'll have no idea which functions do support string lambdas, and which don't, which will leave him in an eternal guessing game of "well, looks like *that* doesn't compile"...

So, if we *do* choose that string lambdas should leave, then I believe they should clearly be marked as "deprecated". We can leave them in forever, but the end user *must* be told that string lambdas are *not* the way it is meant to be used anymore.

--------

Related: My vote goes that they should stay. Sure, normal lambdas are more powerful, but string lambdas are very light weight. Do you really argue that this is better?

sort!((a, b) => a > b)(1, 2, 3);
vs
sort!"a > b"(1, 2, 3);

string lambdas look clearer to me.

Also, and this is important (!): A lambda is *always unique*, whereas strings alias each other. This means that:

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

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

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

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

But
Sorter!((a, b) => a > b) sorter1;
Sorter!((a, b) => a > b) sorter2;
static assert(typeof(sorter1) == typeof(sorter2)); //Fails

--------

So, my vote is that string lambdas should stay. Supporting them is easy: Just an extra alias at the top of the function. Getting rid of them buys us nothing. There are people who use them, and there are cases where they make more sense.

I *AM* 100% perfectly fine with making the default pred a normal lambda though, and promote their use over string lambdas.
August 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.
August 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)); //Fails

Ew. I don't see this as a good argument. Code and strings are fundamentally different.

All the following functionally equivalent string lambdas will produce different instantiations of Sorter:

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

etc.