December 15, 2010
On 12/15/10 4:42 PM, retard wrote:
> Wed, 15 Dec 2010 16:33:43 -0600, Andrei Alexandrescu wrote:
>
>> On 12/15/10 4:18 PM, retard wrote:
>>> Wed, 15 Dec 2010 22:23:35 +0100, Jacob Carlborg wrote:
>>>> Array(1, 2, 3, 4, 5).sortWith(_>   _)
>>>
>>> The first instance of _ (from left to right) is replaced with the first
>>> element of the parameter tuple, the second with second element, etc.
>>>
>>> This is actually very useful since many lambdas only use 1-2
>>> parameters. It has its limitations. For example referring to the same
>>> parameter requires a named parameter or some other hack. Combined with
>>> Haskell style partial application this allows stuff like:
>>>
>>> Array(1, 2, 3, 4, 5).foreach { println }
>>>
>>> Array(1, 2, 3, 4, 5).filter(2<)
>>
>> For short lambdas I prefer Phobos' convention of using "a" and "b", e.g.
>> "2<  a" or "a<  b". Since it's a string, "_<  _" would have been usable
>> with Phobos too but I wouldn't like such a change.
>
> I haven't have time to test this (still using D1 + Tango), but these
> magical 'a' and 'b' make me wonder whether there are any namespace
> issues. Can you refer to symbols defined in the current module or in the
> Phobos module the collection function is declared? Do the 'a' and 'b'
> shadow some other instances of 'a' and 'b'?

There are no hygiene issues, but lookup is limited to the modules included in std.functional. That's goes well with the charter of short lambdas - I mean, they are short :o).

Andrei
December 15, 2010
Wed, 15 Dec 2010 16:47:01 -0600, Andrei Alexandrescu wrote:

> On 12/15/10 4:42 PM, retard wrote:
>> Wed, 15 Dec 2010 16:33:43 -0600, Andrei Alexandrescu wrote:
>>
>>> On 12/15/10 4:18 PM, retard wrote:
>>>> Wed, 15 Dec 2010 22:23:35 +0100, Jacob Carlborg wrote:
>>>>> Array(1, 2, 3, 4, 5).sortWith(_>   _)
>>>>
>>>> The first instance of _ (from left to right) is replaced with the first element of the parameter tuple, the second with second element, etc.
>>>>
>>>> This is actually very useful since many lambdas only use 1-2 parameters. It has its limitations. For example referring to the same parameter requires a named parameter or some other hack. Combined with Haskell style partial application this allows stuff like:
>>>>
>>>> Array(1, 2, 3, 4, 5).foreach { println }
>>>>
>>>> Array(1, 2, 3, 4, 5).filter(2<)
>>>
>>> For short lambdas I prefer Phobos' convention of using "a" and "b", e.g. "2<  a" or "a<  b". Since it's a string, "_<  _" would have been usable with Phobos too but I wouldn't like such a change.
>>
>> I haven't have time to test this (still using D1 + Tango), but these magical 'a' and 'b' make me wonder whether there are any namespace issues. Can you refer to symbols defined in the current module or in the Phobos module the collection function is declared? Do the 'a' and 'b' shadow some other instances of 'a' and 'b'?
> 
> There are no hygiene issues, but lookup is limited to the modules included in std.functional. That's goes well with the charter of short lambdas - I mean, they are short :o).

Ha, that's the thing I was after. Hopefully it's well documented. No need to answer, I can check it myself.
December 16, 2010
On 15.12.2010 17:37, Andrej Mitrovic wrote:
> On 12/15/10, Stephan Soller<stephan.soller@helionweb.de>  wrote:
>>
>> So, _if_ we can figure out a way to do some nice chaining of such calls
>> we can get:
>>
>> 	[1, 2, 3, 4, 5].filter!((e){ return e>  3; })
>> 		.map!((e){ return e*e; });
>>
>> Or if you don't like delegates and don't mind obvious compile time black
>> magic:
>>
>> 	[1, 2, 3, 4, 5].filter!("a>  3").map!("a * a");
>>
>
> You might use pipe! as an alternative:
>
> import std.algorithm;
> import std.functional;
> import std.stdio;
>
> void main()
> {
>      int[] arr = [1, 2, 3, 4, 5];
>      auto result = pipe!( filter!("a>3"), map!("a * a") )(arr);
>      writeln(result);
> }
>
> But I can't seem to do the same with delegates:
>
> import std.algorithm;
> import std.functional;
> import std.stdio;
>
> void main()
> {
>      int[] arr = [1, 2, 3, 4, 5];
>      pipe!( filter!( (e){ return e>  3; } ),
>               map!( (e){ return e*e; } ) )(arr);
> }
>
> chain.d(9): Error: expression template filter(Range) is not a valid
> template value argument
> chain.d(9): Error: expression template map(Range) is not a valid template
> value argument

Thanks for testing this out. :)

I noticed compose! and pipe! but these break my "thought flow". It's hard to describe since I only experienced such a way of writing code in Ruby. You can more or less write your thoughts down without much translation or the need to go back and forth in the line. As you think you can type… I don't need a "thought buffer" so to speak.

Having to think about if I want to call multiple collection operations or just one right at the start breaks this flow. That the data at hand is pushed to the end of the line (poor visibility) also don't really help. I don't know if this is a matter preference but while deciding on such collection operations I usually think about the data first. Only if I know how the data is structured I start to apply operations on it.

This code

	[1, 2, 3, 4, 5].filter!("a>  3").map!("a * a");

matches the that flow of thoughts. With pipe! I have to jump 2 times within a buffer of 3 thoughts.

All that may sound very… unprofessional or "not like a real programmer". However that kind of "mental friction" adds up and you notice that if it's no longer there. I really agree with Matz (the creator of Ruby) that the way you formulate your thoughts changes your thought patterns in turn. Ruby is carefully crafted to keep the resulting thought patterns close to the natural ones. To experienced programmers this might be irritating at first since languages like C++, Java and even D require some mental acrobatics to formulate what you want. It takes some time to just think strait again if you're used to these acrobatics. Ruby is a very interesting subject if you're interested in what programmers think while programming.

Please note that this stuff is IMHO limited to application programming. That is to solve hight level problems where low level work only adds friction and is not that important (like with usual Websites). With systems level programming this is different. When working with low-level APIs and very machine specific problems the thought patterns are different than the ones of application programming. The problems are often not defined by real world needs and therefore the experience from the real world can not help there.

This is the reason why I think D should not spend to much time with this. It's not a design goal of D and probably would collide with other design goals (e.g. efficient compiler implementation). If someone really wants this kind of programming in D it's not that difficult to do, but will cost efficiency (e.g. using real functions instead of templates). D should not mimic Ruby nor is Ruby as bad as some might think. It's one of the few languages that really accept that programmers are humans and not machines. D does this too but by far not to the same extend (thats why I still like D1 more than D2 actually).

Ruby has its applications and D has its applications. The right tool for the right job only works if the different tools have a distinct purpose and not try to do everything at the expense of being good at one thing.

Happy programming
Stephan Soller
December 16, 2010
On Wed, 15 Dec 2010 17:13:48 -0500, Nick Sabalausky wrote:
> Feel free not to answer that though, I don't think
> either of us are feeling particularly inclined to end up in a heated
> debate about it today.

Yes, peace. I also understand you and agree that there's an issue with overreactions. However I try to act somehow rationally  ;)

Cheers
Piotrek
December 16, 2010
On 12/16/10, Stephan Soller <stephan.soller@helionweb.de> wrote:
> snip

The cool thing about D is that with a little bit of string magic you can make your own DSL's. Here's a rather hardcoded and superficial example, but for this simple case it works:

http://pastebin.com/Xkghv1ky

Of course you'd need to build your own little DSL string parsing functions and use regex instead of hardcoding it like that. But all kinds of syntaxes are possible.
December 16, 2010
On 2010-12-15 17:06, Stephan Soller wrote:
> On 14.12.2010 20:03, Jacob Carlborg wrote:
>> On 2010-12-14 19:33, Stephan Soller wrote:
>>>>>
>>>>> I think it's a matter of consistency. In Ruby blocks are used all the
>>>>> time for pretty much everything. In D this isn't the case because
>>>>> usually templates are used for stuff where blocks are used in Ruby
>>>>> (e.g.
>>>>> map, group and find in std.algorithm).
>>>>
>>>> I think that the templates that take a string as a predicate is just an
>>>> ugly hack because D has a too verbose delegate syntax.
>>>>
>>>
>>> I absolutely agree with that. However I don't have a better idea how to
>>> write it. Delegate syntax is already fairly compact in D.
>>>
>>> For example code as this isn't very uncommon in Ruby:
>>>
>>> [1, 2, 3, 4, 5].select{|e| e > 3}.collect{|e| e*e}
>>>
>>> Of course this is a simplified example. Usually the collection would
>>> contain some objects and the blocks would filter for a method call (like
>>> "e.even?"). However I built a small D1 struct that implements a select
>>> and collect function. With the unified function call synatax for array
>>> with code should actually work:
>>>
>>> [1, 2, 3, 4, 5].select((int e){ return e > 3; })
>>> .collect((int e){ return e*e; });
>>>
>>> It's already fairly compact. The main overhead is the parameter type and
>>> the return statement. I don't believe this can be reduced any more
>>> without breaking consistency of the language.
>>
>> Probably not, but, for example, Scala allows very compact delegate
>> literals:
>>
>> Array(1, 2, 3, 4, 5).select(_ > 3).collect(_ * _)
>>
>> Or more verbose:
>>
>> Array(1, 2, 3, 4, 5).select((x) => x > 3).collect((x, y) => x * y)
>>
>> I'm not 100% sure I that the syntax is correct.
>>
>>> Delegates are way more verbose in other languages like PHP and
>>> JavaScript (more or less the same syntax in both languages). Despite
>>> that it's no problem to use it and in case of jQuery it's used very
>>> often. I think the main reason why delegates are not used like that in D
>>> is performance. I'm really not sure about it but I suspect that
>>> delegates are less effective than code directly generated by a template
>>> like map!(). I don't know how efficient templates can integrate
>>> delegates so I just suspect that this is a performance problem.
>>>
>>> Happy programming
>>> Stephan Soller
>>
>> PHP has a very verbose delegate syntax with explicit closures, one of
>> the many reasons I don't use PHP. JavaScript has quite similar syntax as
>> D but the "function" keyword is required, I try to use CoffeeScript
>> (compiles to javascript), which has a lot nicer delegate syntax, as
>> often I can.
>>
>> D(dmd) needs to be able to inline delegates.
>>
>
> I'm not so much concerned about verbose delegate syntax. Personally I
> don't mind using the current delegate syntax with templates like map!.
> However I'm concerned about performance and parameter order. Does
> someone know how delegates within templates are handled? I looked at the
> source of std.algorithm and std.functional but I'm still not sure what
> actually happens if you call something like
>
> auto ary = [1, 2, 3];
> map!((e){ return e*e; })(ary);
>
> I can't test this code right now since I don't have a D2 compiler
> installed at this computer but the std.algorithm source contains unit
> tests with delegates.
>
> If that now works with uniform function call syntax it could be written
> like that:
>
> auto ary = [1, 2, 3];
> ary.map!((e){ return e*e; });
>
> I don't know if chaining of such calls is possible but the above syntax
> is already pretty good. Sure, it's not perfect and the "return" still is
> some overhead, but I don't think it needs work. What's more important
> (at least for me) is the chaining ability and how performant such
> delegates actually are. From what I understood from std.functional it
> wraps string expressions in delegates anyway so using
>
> ary.map!("a*a");
>
> is not more efficient. Please correct me if I'm wrong! The
> std.functional code is definitely above my understanding.

I would though it just mixed in the string with some other code to make a complete expression.

> So, _if_ we can figure out a way to do some nice chaining of such calls
> we can get:
>
> [1, 2, 3, 4, 5].filter!((e){ return e > 3; })
> .map!((e){ return e*e; });
>
> Or if you don't like delegates and don't mind obvious compile time black
> magic:
>
> [1, 2, 3, 4, 5].filter!("a > 3").map!("a * a");
>
> I think if chaining would work like that D would already be pretty close
> to the expressive power of Ruby. It's just that this kind of programming
> is very dependent on the ability of built in collections and libraries
> that allow such a programming style.
>
> Happy programming
> Stephan Soller

-- 
/Jacob Carlborg
December 16, 2010
On 2010-12-15 23:33, Andrei Alexandrescu wrote:
> On 12/15/10 4:18 PM, retard wrote:
>> Wed, 15 Dec 2010 22:23:35 +0100, Jacob Carlborg wrote:
>>> Array(1, 2, 3, 4, 5).sortWith(_> _)
>>
>> The first instance of _ (from left to right) is replaced with the first
>> element of the parameter tuple, the second with second element, etc.
>>
>> This is actually very useful since many lambdas only use 1-2 parameters.
>> It has its limitations. For example referring to the same parameter
>> requires a named parameter or some other hack. Combined with Haskell
>> style partial application this allows stuff like:
>>
>> Array(1, 2, 3, 4, 5).foreach { println }
>>
>> Array(1, 2, 3, 4, 5).filter(2<)
>
> For short lambdas I prefer Phobos' convention of using "a" and "b", e.g.
> "2 < a" or "a < b". Since it's a string, "_ < _" would have been usable
> with Phobos too but I wouldn't like such a change.
>
> Andrei

The point here isn't that we want "a" and "b" to be replaced with "_" the point is that we want to get rid of the string and have a shorter and less verbose syntax for delegate literals.

-- 
/Jacob Carlborg
December 16, 2010
On 12/16/10 1:30 PM, Jacob Carlborg wrote:
> On 2010-12-15 23:33, Andrei Alexandrescu wrote:
>> On 12/15/10 4:18 PM, retard wrote:
>>> Wed, 15 Dec 2010 22:23:35 +0100, Jacob Carlborg wrote:
>>>> Array(1, 2, 3, 4, 5).sortWith(_> _)
>>>
>>> The first instance of _ (from left to right) is replaced with the first
>>> element of the parameter tuple, the second with second element, etc.
>>>
>>> This is actually very useful since many lambdas only use 1-2 parameters.
>>> It has its limitations. For example referring to the same parameter
>>> requires a named parameter or some other hack. Combined with Haskell
>>> style partial application this allows stuff like:
>>>
>>> Array(1, 2, 3, 4, 5).foreach { println }
>>>
>>> Array(1, 2, 3, 4, 5).filter(2<)
>>
>> For short lambdas I prefer Phobos' convention of using "a" and "b", e.g.
>> "2 < a" or "a < b". Since it's a string, "_ < _" would have been usable
>> with Phobos too but I wouldn't like such a change.
>>
>> Andrei
>
> The point here isn't that we want "a" and "b" to be replaced with "_"
> the point is that we want to get rid of the string and have a shorter
> and less verbose syntax for delegate literals.

I understand. Using strings is witnessing the fact that we couldn't find a shorter syntax that didn't have problems. That being said, it's very possible there are some great ones, we just couldn't find them.

Andrei
December 16, 2010
"Andrej Mitrovic" <andrej.mitrovich@gmail.com> wrote:
> The cool thing about D is that with a little bit of string magic you can make your own DSL's. Here's a rather hardcoded and superficial example, but for this simple case it works:
>
> http://pastebin.com/Xkghv1ky
>
> Of course you'd need to build your own little DSL string parsing functions and use regex instead of hardcoding it like that. But all kinds of syntaxes are possible.

You are right. I tried making one. It parses things like sum !(q{  i=0:10;
i * sum!(q{   j=0:10, j!=i;    i*j   })  })
http://pastebin.com/mQaKXaYY -
But it doesn't work because not being CTFE friendly, and also syntax parser
is ugly because I am not strong at syntax parsing.
Note that creating good syntax parser in CTFE is rather hard, especially for
complex syntaxes like of D expressions.
Oh, if CTFE could:
 1) run external process
 2) do file i/o
 3) use dynamic libraries
This would be much much simpler, but I guess it will not.
This features also has safety issues, but D is system language, not browser
one.


December 16, 2010
"Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:iedqos$787$1@digitalmars.com...
> On 12/16/10 1:30 PM, Jacob Carlborg wrote:
>>
>> The point here isn't that we want "a" and "b" to be replaced with "_" the point is that we want to get rid of the string and have a shorter and less verbose syntax for delegate literals.
>
> I understand. Using strings is witnessing the fact that we couldn't find a shorter syntax that didn't have problems. That being said, it's very possible there are some great ones, we just couldn't find them.
>

Any problem with the other Scala/C#-style one?:

(x, y) =>  x * y

// Lowered to:

(x, y) { return x * y; }

(Maybe that was rejected before due the the weird float operators that are now being ditched?)

It wouldn't be used for delegates that involve actual statements (it would be expression-only), but that fits with the whole point of a lambda expression.

Also, unlike the strings it doesn't suffer the problem of being evaluated in the wrong scope. For instance I think it's perfctly sensible to want a short lambda to be able to do something like this (pardon me if I have the syntax for map() wrong):

int foo(int x) { ... }
collection.map!"foo(a) + 3"();
// Or if you want proper syntax highlighting:
collection.map!q{ foo(a) + 3 }();

I think that totally fits the charter of short lambdas, but the strings just can't do it (at least not without turning map() into something that needs to be mixed in). The lowered Scala/C#-style would be able to though:

int foo(int x) { ... }
collection.map!((a) => foo(a) + 3)();
// Lowered to the messier:
collection.map!((a) { return foo(a) + 3; })();