December 25, 2011
On 12/24/2011 10:37 PM, Kapps wrote:
> The two biggest things that I believe D would benefit from, in terms of readability, are UFCS and C#-style lambdas (which were already discussed).
> 
> Something like
> 'array(find("test", select!("a.name")(filter!("a.num > 3")(Elements))))'
> Could be rewritten much cleaner as
> Elements.filter!"a.num > 3"
>     .select!"a.name"
>     .find("test")
>     .array();
> 
> The first one is very confusing, hard to split across multiple lines without losing meaning, and difficult to see what it even operates on (assume Elements is not an array and thus can not be used with the current implementation of UFCS). The second one, you can clearly see operates on Elements, you can clearly split it across multiple lines, and it's very obvious where things stop and end. Plus, it eliminates at least one set of parenthesis per function call, not to mention being much easier to write as it's in order of steps instead of opposite order.
> 

Pardon, what does UFCS stand for?
December 25, 2011
Universal Function Call Syntax. C# calls them extension methods.
Basically, the ability to call a method directly on a class (in the form of A.MyMethod) without it being declared in the class. Usually just a static/global method that gets rewritten. Instead of MyMethod(A), you can use A.MyMethod.

On 25/12/2011 1:51 AM, Chad J wrote:
> On 12/24/2011 10:37 PM, Kapps wrote:
>> The two biggest things that I believe D would benefit from, in terms of
>> readability, are UFCS and C#-style lambdas (which were already discussed).
>>
>> Something like
>> 'array(find("test", select!("a.name")(filter!("a.num>  3")(Elements))))'
>> Could be rewritten much cleaner as
>> Elements.filter!"a.num>  3"
>>      .select!"a.name"
>>      .find("test")
>>      .array();
>>
>> The first one is very confusing, hard to split across multiple lines
>> without losing meaning, and difficult to see what it even operates on
>> (assume Elements is not an array and thus can not be used with the
>> current implementation of UFCS). The second one, you can clearly see
>> operates on Elements, you can clearly split it across multiple lines,
>> and it's very obvious where things stop and end. Plus, it eliminates at
>> least one set of parenthesis per function call, not to mention being
>> much easier to write as it's in order of steps instead of opposite order.
>>
>
> Pardon, what does UFCS stand for?

December 25, 2011
On 12/25/2011 02:57 AM, Kapps wrote:
> Universal Function Call Syntax. C# calls them extension methods. Basically, the ability to call a method directly on a class (in the form of A.MyMethod) without it being declared in the class. Usually just a static/global method that gets rewritten. Instead of MyMethod(A), you can use A.MyMethod.
> 
> On 25/12/2011 1:51 AM, Chad J wrote:
>> On 12/24/2011 10:37 PM, Kapps wrote:
>>> The two biggest things that I believe D would benefit from, in terms of readability, are UFCS and C#-style lambdas (which were already discussed).
>>>
>>> Something like
>>> 'array(find("test", select!("a.name")(filter!("a.num>  3")(Elements))))'
>>> Could be rewritten much cleaner as
>>> Elements.filter!"a.num>  3"
>>>      .select!"a.name"
>>>      .find("test")
>>>      .array();
>>>
>>> The first one is very confusing, hard to split across multiple lines without losing meaning, and difficult to see what it even operates on (assume Elements is not an array and thus can not be used with the current implementation of UFCS). The second one, you can clearly see operates on Elements, you can clearly split it across multiple lines, and it's very obvious where things stop and end. Plus, it eliminates at least one set of parenthesis per function call, not to mention being much easier to write as it's in order of steps instead of opposite order.
>>>
>>
>> Pardon, what does UFCS stand for?
> 

Ah good.

I always thought it a bit odd that we didn't have that.  Better yet if it worked with operator overloading... then the operator overloads could check for null.
December 25, 2011
On Saturday, 24 December 2011 at 20:19:48 UTC, Andrei Alexandrescu wrote:
> Analogies of programming language with human language are flawed and take nowhere interesting. The only text that gets close to the level of precision and non-ambiguity needed is legal text; reading any amount of legal text blunts one's desire to be more like it.
>
> Andrei

I know of one attempt for natural language programming, Hypertalk, the language of the Hypercard multimedia tool Apple developped in the 1990's. The tool was very interesting in itself, but the programming language is very limited and quite verbose due to the choice of being a natural language. I suspect it is the case of all natural languages used as programming languages. Apart maybe in artificial intelligence, I believe natural language has no useful use.
December 25, 2011
On 25.12.2011 00:44, Timon Gehr wrote:
>
> Not really. Functional style code tends to be conceptually simpler.
> Having code that is more readable can help. Getting rid of (({return
> {return}}){return()}) makes the code more readable, whereas excessively
> shortening identifiers does the opposite.
>
> See here for an example of what bearophile is talking about:
> http://pastebin.com/2rEdx0RD

r=cons(st(1UL),cons(st(1UL),lz({return zipWith((Lazy!ulong a,Lazy!ulong b){return lz({return a()+b();});},r,r().tail)();})));

December 25, 2011
On 2011-12-25 02:20, Adam D. Ruppe wrote:
> On Saturday, 24 December 2011 at 23:51:57 UTC, bearophile wrote:
>> Using certain abstractions sometimes helps to write one idea only once
>> in a program. Etc.
>
> This is the biggest one, and applies to all kinds of code.
> I like to write little functions with meaningful names.
>
> bool isOdd(int i) {
> if((i % 2) == 0)
> return false;
> else
> return true;
> }
>
> filter!isOdd(myCollection);
>
>
> I find that nicer than
>
> filter!"i % 2 != 0"(myCollection);
>
> despite it being longer.
>
> With the former, it says very simply that it wants odd
> numbers at the usage location.
>
>
> With the latter, you have to think for a second about
> what the modulus operator actually does and what the
> definition of an odd number is before you can get to
> why the filter is there at all.
>
>
> It gets even worse if the other function has non-trivial
> code. Best to just give it a name so you don't have to think
> about the implementation at all at the usage site.

I completely agree with this. It can be used in if-statements and similar as well.

-- 
/Jacob Carlborg
December 25, 2011
On 2011-12-25 05:44, Andrei Alexandrescu wrote:
> On 12/24/2011 07:20 PM, Adam D. Ruppe wrote:
>> On Saturday, 24 December 2011 at 23:51:57 UTC, bearophile wrote:
>>> Using certain abstractions sometimes helps to write one idea only once
>>> in a program. Etc.
>>
>> This is the biggest one, and applies to all kinds of code.
>> I like to write little functions with meaningful names.
>>
>> bool isOdd(int i) {
>> if((i % 2) == 0)
>> return false;
>> else
>> return true;
>> }
>>
>> filter!isOdd(myCollection);
>>
>>
>> I find that nicer than
>>
>> filter!"i % 2 != 0"(myCollection);
>>
>> despite it being longer.
>
> Different strokes for different folks. IMHO it would be difficult to
> justify the verboseness and the unnecessary (twice) flow of control. I
> see every letter beyond this as a liability:
>
> bool isOdd(int i)
> {
> return (i & 1) != 0;
> }

The point was not how to implement "isOdd", it was to use a symbol instead.

>> With the former, it says very simply that it wants odd
>> numbers at the usage location.
>
> Giving an operation, however trivial, a symbol, is often a good thing.
> There are of course limits, and each engineer has their own ideas where
> to draw the line.
>
>
> Andrei


-- 
/Jacob Carlborg
December 25, 2011
On 12/25/2011 02:44 PM, simendsjo wrote:
> On 25.12.2011 00:44, Timon Gehr wrote:
>>
>> Not really. Functional style code tends to be conceptually simpler.
>> Having code that is more readable can help. Getting rid of (({return
>> {return}}){return()}) makes the code more readable, whereas excessively
>> shortening identifiers does the opposite.
>>
>> See here for an example of what bearophile is talking about:
>> http://pastebin.com/2rEdx0RD
>
> r=cons(st(1UL),cons(st(1UL),lz({return zipWith((Lazy!ulong a,Lazy!ulong
> b){return lz({return a()+b();});},r,r().tail)();})));
>

http://pastebin.com/C6vf9DQQ

r=cons(st(cast(T)1),lz({return merge(merge(map((Lazy!T a){return lz({return 2*a();});},r),map((Lazy!T a){return lz({return 3*a();});},r)),map((Lazy!T a){return lz({return 5*a();});},r))();}));

D'oh!
December 25, 2011
On Sunday, 25 December 2011 at 04:44:57 UTC, Andrei Alexandrescu wrote:
> I see every letter beyond this as a liability:

Yeah, though like Jacob said, the implementation doesn't
matter as much as the idea of giving it a name.

But, if you're curious about why I wrote it long form, it's
just a quirk I sometimes do; if I read something out loud
differently than how I wrote it, I'll sometimes rewrite it.
Here, I dictated it to myself as "if it's divisible by two,
return false (not odd), otherwise, return true (is odd)", and
decided to write it almost exactly like that.


Usually, these little things are meaningless once compiled,
but check this out:

===
bool test(int a) {
	if(a%2 == 0)
		return false;
	else
		return true;
}

bool test2(int a) {
	return a&1;
}
===

$ dmd -O -c test.d # 2.057 btw
$ objdump -d test.o
Disassembly of section .text._D5test24testFiZb:

00000000 <_D5test24testFiZb>:
  0:   55                      push   %ebp
  1:   8b ec                   mov    %esp,%ebp
  3:   50                      push   %eax
  4:   99                      cltd
  5:   33 c2                   xor    %edx,%eax
  7:   25 01 00 00 00          and    $0x1,%eax
  c:   03 d0                   add    %eax,%edx
  e:   83 fa 01                cmp    $0x1,%edx
 11:   19 c0                   sbb    %eax,%eax
 13:   8b e5                   mov    %ebp,%esp
 15:   40                      inc    %eax
 16:   5d                      pop    %ebp
 17:   c3                      ret

Disassembly of section .text._D5test25test2FiZb:

00000000 <_D5test25test2FiZb>:
  0:   55                      push   %ebp
  1:   8b ec                   mov    %esp,%ebp
  3:   50                      push   %eax
  4:   25 01 00 00 00          and    $0x1,%eax
  9:   f7 d8                   neg    %eax
  b:   19 c0                   sbb    %eax,%eax
  d:   8b e5                   mov    %ebp,%esp
  f:   f7 d8                   neg    %eax
 11:   5d                      pop    %ebp
 12:   c3                      ret


Almost the same, but not quite.... I think the two
functions should have compiled identically. With gcc
(compiling the code as C), it comes out:

00000000 <test>:
  0:   55                      push   %ebp
  1:   89 e5                   mov    %esp,%ebp
  3:   8b 45 08                mov    0x8(%ebp),%eax
  6:   83 e0 01                and    $0x1,%eax
  9:   5d                      pop    %ebp
  a:   c3                      ret

for both functions.

(btw "Akismet thinks your post looks like spam. " LOL)
December 25, 2011
On 12/25/2011 11:53 PM, Timon Gehr wrote:
> On 12/25/2011 02:44 PM, simendsjo wrote:
>> On 25.12.2011 00:44, Timon Gehr wrote:
>>>
>>> Not really. Functional style code tends to be conceptually simpler.
>>> Having code that is more readable can help. Getting rid of (({return
>>> {return}}){return()}) makes the code more readable, whereas excessively
>>> shortening identifiers does the opposite.
>>>
>>> See here for an example of what bearophile is talking about:
>>> http://pastebin.com/2rEdx0RD
>>
>> r=cons(st(1UL),cons(st(1UL),lz({return zipWith((Lazy!ulong a,Lazy!ulong
>> b){return lz({return a()+b();});},r,r().tail)();})));
>>
>
> http://pastebin.com/C6vf9DQQ
>
> r=cons(st(cast(T)1),lz({return merge(merge(map((Lazy!T a){return
> lz({return 2*a();});},r),map((Lazy!T a){return lz({return
> 3*a();});},r)),map((Lazy!T a){return lz({return 5*a();});},r))();}));
>
> D'oh!

With UFCS and alternate delegate syntax:

r=st(cast(T)1).cons(lz(=> r.map((Lazy!T a)=> lz(=> 2*a()))
                   .merge(r.map((Lazy!T a)=> lz(=> 3*a())))
                   .merge(r.map((Lazy!T a)=> lz(=> 5*a())))()
               ));


With delegate parameter type inference for non-template delegate type parameters (as proposed by Andrei in a recent bug report):

r=st(cast(T)1).cons(lz(=> r.map((a)=> lz(=> 2*a()))
                   .merge(r.map((a)=> lz(=> 3*a())))
                   .merge(r.map((a)=> lz(=> 5*a())))()
               ));


With language support for non-strict evaluation:

@nonstrict:

r=cast(T)1
    .cons(
                 r.map((a)=> 2*a)
          .merge(r.map((a)=> 3*a))
          .merge(r.map((a)=> 5*a))
    );