www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Functional programming in D and some reflexion on the () optionality.

reply deadalnix <deadalnix gmail.com> writes:
It is known that some part of D are very dmd's implementation defined. 
One of the part is manipulation function as first class objects and 
implicitly calling them.

Some digressions about functional programing first.

Functional programming emphasis function purity, function as first class 
objects and immutability (among other things). Now, function is data 
(and can be passed as first class object to other function for instance) 
but no difference exists anymore between data and function.

A function which is pure, with no parameters, will always return the 
same value. The difference between what is a function without argument 
and what is a constant is up to the compiler.

The same way, the difference between a delegate and an expression don't 
exist anymore.

This allow functional style to remove explicit call of functions. In 
fact, when does the function get called is irrelevant, because they are 
pure. This is for instance THE thing is haskell.

The difference between a function call and a delegate creation is 
blurred, don't exists anymore and don't matter anymore regarding program 
result.

D intend to implement functional, and get an mix of all the feature 
cited above, but in a way that don't work properly and is mostly dmd's 
implementation defined.

Let's put aside the  property phenomena, this isn't that important here.

To me, the first big failure of D to implement functional style is to 
not have first class functions. You get a function using & operator. But 
does it really make sense ? See code below :

void foo(){}
void bar(void function() buzz) {}

void main() { bar(foo); } // This will execute foo, and so fail. 
Functions are not first class objects.

void main() {
     auto bar = &foo;
     foo(); // Do something.
     bar(); // Do the same thing.
     auto buzz = &bar;
     (*buzz)(); // Do the same thing.
}

Functions don't behave the same way is they are variables or declared in 
the source code.

Worse, foo was before a function call. Now it isn't anymore. foo, as a 
expression have a different meaning depending on what is done on it. It 
would become very confusing if foo return a reference, so it is an 
lvalue and & is a valid operation on the function call.

As D don't enforce purity like functional programing does, it can't be 
up to the compiler to decide when does the function get executed.

Then come UFCS. UFCS allow for function calls with parameters. It is 
still inconsistent.

void foo(T)(T t) {}

a.foo; // foo is called with a as argument.
&a.foo; // error : not an lvalue

Now let imagine that foo is a member function of a, &a.foo become a 
delegate. a.foo is still a function call. This is still quite inconsistent.

Implementing all this is almost impossible when you add  property into 
the already messy situation. Additionally, the current implement fails 
to provide the basics of functional programing, and break several 
abstraction provided by other languages features. C++ has proven that 
bad association of good language features lead to serious problems.

This require to be formalized in some way and not based on dmd's 
implementation. Inevitably, the process will lead to code breakage 
(adding or removing some ()/&).

Reading the  property thread, it seems that most people want to keep 
dmd's current behavior. Because code rely on it. This make sense, but if 
dmd's implement is the only goal, it means that other compiler are only 
to be reverse engineering dmd's behavior, and are guaranteed to lag 
behind. Considering this, I seriously wonder if it make sense to even 
try to follow dmd's behavior and decide whatever seems the right one 
when writing a D compiler, which will result in community split, or no 
alternative compiler produced for D.

I also have some proposal to fix thing, even some that would allow 
a.map!(...).array() to still be available. But inevitably, some other 
construct will broke. At this point, what matter isn't really what 
solution is adopted, but do we still want to be dependent on dmd 
implementation for D features.
Aug 06 2012
next sibling parent reply travert phare.normalesup.org (Christophe Travert) writes:
deadalnix:
 The same way, the difference between a delegate and an expression don't 
 exist anymore.
int fun(); int fun(int t); One solution would be to find a way that would enable fun to be both a function and its return type, and that would enable 1.fun to be both delegate and its return type. This can be achieved by implicit casting. The type of expression fun is int function(), but if an int is expected, execute fun and use its return value. Like: struct Function { int fun(); alias fun this; } One difference with current behavior would be that "fun;" alone doesn't actually execute the function. Statements consisting of a single function parameterless expression could be made to call the function, although I don't think this is a good idea because it would be an exception in the previous rule, and parenthesis-less call is more a functionnal programming feature, and should IMHO not be used when side-effect is desired. Also, in: auto bar = fun; bar is of type int function(), and not of type int. If the function is pure, it doesn't make great difference, but if the function is impure, then if is called each time bar is present in an expression that requires an int. The same way, the dot operator on a single parameter free or member function would return a delegate, that is implicitely cast to its return value if the return type is expected. This would break code where the use of parenthesis-less function call is hazardous, but hopefully not so much when it is legitimate [1]. What do you think? -- Christophe [1] templates will have to be taken care of: "a.map!(x=>x+1).array" would try to instanciate array with a delegate type, whereas it should be instanciated with a range type. From an the discussion about template on enum type in this newsgroup, there is some opposition to make template instanciate after an implicit cast because of the mess that can arise and the technical difficulties for the compiler. However, if this feature is limitted and controled, I think it can be worth it.
Aug 06 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
Aug 06 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 19:25, Timon Gehr a écrit :
 On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
We already have bizantine language rule here.
Aug 06 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2012 07:54 PM, deadalnix wrote:
 Le 06/08/2012 19:25, Timon Gehr a écrit :
 On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
We already have bizantine
s/bizantine/non-trivial/
 language rule here.
Also note the emphasis on 'unimportant or non-existent'. A reason why those rules exist is probably related to the fact that there are many distinct concepts in the language (including some C heritage) and function declarations try to bridge between them.
Aug 06 2012
prev sibling parent reply travert phare.normalesup.org (Christophe Travert) writes:
Timon Gehr , dans le message (digitalmars.D:174329), a écrit :
 On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
What exactly do you consider byzantine here, whatever that means? Implicit cast is an already defined feature. Clarifying the way parenthesis-less function calls exist by adding a casting rule is making the langage more simple IMHO, and might improve the current position. Of course, if your point is that parenthesis-less function calls are unimportant or non-existent, then I understand your point of view, but other people seems to think differently. -- Christophe
Aug 06 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 21:42, Christophe Travert a écrit :
 Timon Gehr , dans le message (digitalmars.D:174329), a écrit :
 On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
What exactly do you consider byzantine here, whatever that means? Implicit cast is an already defined feature. Clarifying the way parenthesis-less function calls exist by adding a casting rule is making the langage more simple IMHO, and might improve the current position. Of course, if your point is that parenthesis-less function calls are unimportant or non-existent, then I understand your point of view, but other people seems to think differently.
auto opDispatch(string name, T, U...)(T delegate() dg, U u) { mixin("return dg()." ~ name ~ "(u);"); } I was thinking about something along that line instead of adding again more language feature. But that really isn't that important. Being able to call or not without () isn't a that big deal. The mess created is.
Aug 06 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2012 09:42 PM, Christophe Travert wrote:
 Timon Gehr , dans le message (digitalmars.D:174329), a écrit :
 On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
What exactly do you consider byzantine here, whatever that means?
byzantine means involved. Why deliberately make the language more complicated for no gain whatsoever?
 Implicit cast is an already defined feature. Clarifying the way
 parenthesis-less function calls exist by adding a casting rule is making
 the langage more simple IMHO,
I don't know what to respond to this. Are you serious?
 and might improve the current position. Of
 course, if your point is that parenthesis-less function calls are
 unimportant or non-existent,
It isn't. My point is that there actually is no issue that would require some complex solution.
 then I understand your point of view, but other people seems to think
differently.
Aug 06 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 22:45, Timon Gehr a écrit :
 It isn't. My point is that there actually is no issue that would
 require some complex solution.
Actually, we have a very complex solution with no issue. That is in itself, an issue, especially when that very complex solution isn't defined properly anywhere except in dmd's implementation.
Aug 06 2012
prev sibling parent travert phare.normalesup.org (Christophe Travert) writes:
Timon Gehr , dans le message (digitalmars.D:174361), a écrit :
 On 08/06/2012 09:42 PM, Christophe Travert wrote:
 Timon Gehr , dans le message (digitalmars.D:174329), a écrit :
 On 08/06/2012 07:20 PM, Christophe Travert wrote:
 What do you think?
Creating byzantine language rules to cater to unimportant or non-existent use cases will slaughter the language.
What exactly do you consider byzantine here, whatever that means?
byzantine means involved. Why deliberately make the language more complicated for no gain whatsoever?
 Implicit cast is an already defined feature. Clarifying the way
 parenthesis-less function calls exist by adding a casting rule is making
 the langage more simple IMHO,
I don't know what to respond to this. Are you serious?
 and might improve the current position. Of
 course, if your point is that parenthesis-less function calls are
 unimportant or non-existent,
It isn't. My point is that there actually is no issue that would require some complex solution.
Misha's post reminded me that parenthesis-less function call rules are not that complicated, although I think he omitted one or a few things like assigning a function to an auto parameter or passing a function to a template. I agree that the situation is sufficent can be kept that way, and that there is no big issue. However, I had the impression from deadalnix' post that what he wanted to discuss was something that would make function real first class types, that you could use, assign, etc, without using function pointers (although there have to be function pointers internally of course). If foo is a function, I think making "auto a = foo;" be a function is something that is expected in a langage where function are first class types. Thus, making parenthesis-less function expressions be functions. I maintain that I don't think that one implicit cast rule is not more complicated than the lines Misha used to describe parenthesis-less function call + some probably missing cases. I understand the D language does not like implicit cast. The approach was to make as few implicit casts as possible and loosen the rule parsimoniously to avoid to create mess. That rule may be more important than making function first-class types in D, or defining parenthesis-less function call as an implicit cast. Now, a little off-topic, implicit cast and rewrites are already everywhere in the language. Many feature are already attempts something, if it does not work, rewrite and attempt again. That's how the compiler works in many cases from what I briefly caught. Describing as many language features as possible as rewrites is a way that may make the language easier, and provides a way to describe feature interactions too, by prioritizing or restricting combinaisons of rewrites. Independently of parenthesis-less function call issue, I think that this may be a way to redefine the language, and open doors to clarify a few tricky issues. I may very well be mistaken, but I think this approach should be considered when discussing language features. -- Christophe
Aug 07 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 19:20, Christophe Travert a écrit :
 What do you think?
The implicit cast is likely to create madness, especially for template instantiation.
Aug 06 2012
prev sibling next sibling parent reply "Mafi" <mafi example.org> writes:
On Monday, 6 August 2012 at 16:40:02 UTC, deadalnix wrote:
 It is known that some part of D are very dmd's implementation 
 defined. One of the part is manipulation function as first 
 class objects and implicitly calling them.
...
 To me, the first big failure of D to implement functional style 
 is to not have first class functions. You get a function using 
 & operator. But does it really make sense ? See code below :

 void foo(){}
 void bar(void function() buzz) {}

 void main() { bar(foo); } // This will execute foo, and so 
 fail. Functions are not first class objects.

 void main() {
     auto bar = &foo;
     foo(); // Do something.
     bar(); // Do the same thing.
     auto buzz = &bar;
     (*buzz)(); // Do the same thing.
 }
This comes from the 'real' function type is 'void f()' whose usage is deprecated in D except for any function-/method-declaration: Imagine a C-style declaration of the fuction type and you'll come to the conclusion it's the way one defines functions in C/C++/D. But this type has a big quirk: it's variable length because different fuctions with the same signature can have a different amount of machine instuctions. Therefore this can't be stored, passed or returned. You can't have array of them. The only exception is when you define a constant, then it's a funciton definition. Now because of this there is a need for another type equivalent to the function itself: its address. It's written 'void (*f)()' in C. This type is mostly reffered to as function even though it's only its address. Now D wants to go away from C declarations so the 'void function()' syntax was invented. Regardless of its name it's only the pointer to the actual functions even though D allows you to call it without dereferencing.
 Functions don't behave the same way is they are variables or 
 declared in the source code.
This is because the former a function pointer and the latter are actual funtions.
 Worse, foo was before a function call. Now it isn't anymore. 
 foo, as a expression have a different meaning depending on what 
 is done on it. It would become very confusing if foo return a 
 reference, so it is an lvalue and & is a valid operation on the 
 function call.
Because any usage of a function except a call or address-taking are complete non-sense, the functions is implicitly called if it hasn't got any parameters.
 As D don't enforce purity like functional programing does, it 
 can't be up to the compiler to decide when does the function 
 get executed.

 Then come UFCS. UFCS allow for function calls with parameters. 
 It is still inconsistent.

 void foo(T)(T t) {}

 a.foo; // foo is called with a as argument.
 &a.foo; // error : not an lvalue
Even though it's written like that, the function is not called with the member calling convention. You can't create a transperent delegate of it because the function does not expect to get 'a' as a 'this'-parameter.
 Now let imagine that foo is a member function of a, &a.foo 
 become a delegate. a.foo is still a function call. This is 
 still quite inconsistent.
Maybe it is. But it comes from the fact that ufcs is an afterthought.
 Implementing all this is almost impossible when you add 
  property into the already messy situation. Additionally, the 
 current implement fails to provide the basics of functional 
 programing, and break several abstraction provided by other 
 languages features. C++ has proven that bad association of good 
 language features lead to serious problems.

 This require to be formalized in some way and not based on 
 dmd's implementation. Inevitably, the process will lead to code 
 breakage (adding or removing some ()/&).

 Reading the  property thread, it seems that most people want to 
 keep dmd's current behavior. Because code rely on it. This make 
 sense, but if dmd's implement is the only goal, it means that 
 other compiler are only to be reverse engineering dmd's 
 behavior, and are guaranteed to lag behind. Considering this, I 
 seriously wonder if it make sense to even try to follow dmd's 
 behavior and decide whatever seems the right one when writing a 
 D compiler, which will result in community split, or no 
 alternative compiler produced for D.
I don't like authorative formal specs. It means most things are set in stone and you have to write a new spec every once in a while which slows down development of awesome language features.
 I also have some proposal to fix thing, even some that would 
 allow a.map!(...).array() to still be available. But 
 inevitably, some other construct will broke. At this point, 
 what matter isn't really what solution is adopted, but do we 
 still want to be dependent on dmd implementation for D features.
Mafi
Aug 06 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 20:01, Mafi a écrit :
 This comes from the 'real' function type is 'void f()' whose usage is
 deprecated in D except for any function-/method-declaration:
 Imagine a C-style declaration of the fuction type and you'll come to the
 conclusion it's the way one defines functions in C/C++/D. But this type
 has a big quirk: it's variable length because different fuctions with
 the same signature can have a different amount of machine instuctions.
Well, foo is either that either a function call. When it is which is quite unclear.
 Therefore this can't be stored, passed or returned. You can't have array
 of them. The only exception is when you define a constant, then it's a
 funciton definition. Now because of this there is a need for another
 type equivalent to the function itself: its address. It's written 'void
 (*f)()' in C. This type is mostly reffered to as function even though
 it's only its address. Now D wants to go away from C declarations so the
 'void function()' syntax was invented. Regardless of its name it's only
 the pointer to the actual functions even though D allows you to call it
 without dereferencing.
No dereferencing is done in the compiled code anyway. BTW, an « address of » operation is done on the type mentioned above.
 Maybe it is. But it comes from the fact that ufcs is an afterthought.
So we have already started to pill up feature that don't integrate with each other C++ style.
 I don't like authorative formal specs. It means most things are set in
 stone and you have to write a new spec every once in a while which slows
 down development of awesome language features.
This isn't about awesome language features. This is about function calls ! The most basic feature ever. BTW, not stabilized feature shouldn't appear in what we call stable release . . . They should be provided testing versions of the compiler so they can be tortured and refined to the point everything make sense.
Aug 06 2012
parent reply "Mafi" <mafi example.org> writes:
On Monday, 6 August 2012 at 18:29:38 UTC, deadalnix wrote:
 Le 06/08/2012 20:01, Mafi a écrit :
 This comes from the 'real' function type is 'void f()' whose 
 usage is
 deprecated in D except for any function-/method-declaration:
 Imagine a C-style declaration of the fuction type and you'll 
 come to the
 conclusion it's the way one defines functions in C/C++/D. But 
 this type
 has a big quirk: it's variable length because different 
 fuctions with
 the same signature can have a different amount of machine 
 instuctions.
Well, foo is either that either a function call. When it is which is quite unclear.
Well, I'd say it's quite clear. It always does what's explicitly written. The only exception is when one tries to do something except calling or address-taking with a function (not a function pointer).
 No dereferencing is done in the compiled code anyway. BTW, an 
 « address of » operation is done on the type mentioned above.
I don't get what you are saying here. For me there's an inherent difference between a fuction and a function pointer. You shouldn't try to conflate them in a system language in my opinion.
 Maybe it is. But it comes from the fact that ufcs is an 
 afterthought.
So we have already started to pill up feature that don't integrate with each other C++ style.
I have to admit one could say this. But there's definitly a big difference between real members and ufcs which would be really hard to design around if one wants to stay in the C family.
 I don't like authorative formal specs. It means most things 
 are set in
 stone and you have to write a new spec every once in a while 
 which slows
 down development of awesome language features.
This isn't about awesome language features. This is about function calls ! The most basic feature ever. BTW, not stabilized feature shouldn't appear in what we call stable release . . . They should be provided testing versions of the compiler so they can be tortured and refined to the point everything make sense.
This isn't about versioning schemes either! But there have been long discussions recently and at least some things will change with the development after 2.060 afaik.
Aug 06 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 21:07, Mafi a écrit :
 No dereferencing is done in the compiled code anyway. BTW, an «
 address of » operation is done on the type mentioned above.
I don't get what you are saying here. For me there's an inherent difference between a fuction and a function pointer. You shouldn't try to conflate them in a system language in my opinion.
On any hardware, a function call will boil down to set up stuff on the stack and branching, jumping, calling or whatever on the ADDRESS of the function's code. In other term, what is generated always involve the function's instruction address. If you dereference that address, you get instructions, but that hardly something useful to have at higher levels because you can"t do anything with it anyway.
 Maybe it is. But it comes from the fact that ufcs is an afterthought.
So we have already started to pill up feature that don't integrate with each other C++ style.
I have to admit one could say this. But there's definitly a big difference between real members and ufcs which would be really hard to design around if one wants to stay in the C family.
Well D also advertise itself as OO and functional. Obviously, decision will have to be made. Because if no design decision is made, you ends up like it is now : micro decision are made when coding, and no overall is followed.
 I don't like authorative formal specs. It means most things are set in
 stone and you have to write a new spec every once in a while which slows
 down development of awesome language features.
This isn't about awesome language features. This is about function calls ! The most basic feature ever. BTW, not stabilized feature shouldn't appear in what we call stable release . . . They should be provided testing versions of the compiler so they can be tortured and refined to the point everything make sense.
This isn't about versioning schemes either! But there have been long discussions recently and at least some things will change with the development after 2.060 afaik.
Yes, that was a digression. When stating that UFCS don't integrate nicely with other features, this showed the need for incubation of feature, and as recent discussions have been made on versioning, I made a bridge.
Aug 06 2012
parent reply "Mafi" <mafi example.org> writes:
On Monday, 6 August 2012 at 19:45:17 UTC, deadalnix wrote:
 Le 06/08/2012 21:07, Mafi a écrit :
 No dereferencing is done in the compiled code anyway. BTW, an 
 «
 address of » operation is done on the type mentioned above.
I don't get what you are saying here. For me there's an inherent difference between a fuction and a function pointer. You shouldn't try to conflate them in a system language in my opinion.
On any hardware, a function call will boil down to set up stuff on the stack and branching, jumping, calling or whatever on the ADDRESS of the function's code. In other term, what is generated always involve the function's instruction address. If you dereference that address, you get instructions, but that hardly something useful to have at higher levels because you can"t do anything with it anyway.
It's the same with an int on the stack, isn't it (literally on the stack; not optimized into a register). To access it, one has to dereference its address but you wouldn't like conflating int and int*. The difference is between the address being a constant (maybe relative to the stack) and being totally unknown. In my opinion this is a big difference, also for functions. ...
 Well D also advertise itself as OO and functional. Obviously, 
 decision will have to be made. Because if no design decision is 
 made, you ends up like it is now : micro decision are made when 
 coding, and no overall is followed.

 I don't like authorative formal specs. It means most things 
 are set in
 stone and you have to write a new spec every once in a while 
 which slows
 down development of awesome language features.
This isn't about awesome language features. This is about function calls ! The most basic feature ever. BTW, not stabilized feature shouldn't appear in what we call stable release . . . They should be provided testing versions of the compiler so they can be tortured and refined to the point everything make sense.
This isn't about versioning schemes either! But there have been long discussions recently and at least some things will change with the development after 2.060 afaik.
Yes, that was a digression. When stating that UFCS don't integrate nicely with other features, this showed the need for incubation of feature, and as recent discussions have been made on versioning, I made a bridge.
Aug 07 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 07/08/2012 14:15, Mafi a écrit :
 On Monday, 6 August 2012 at 19:45:17 UTC, deadalnix wrote:
 Le 06/08/2012 21:07, Mafi a écrit :
 No dereferencing is done in the compiled code anyway. BTW, an «
 address of » operation is done on the type mentioned above.
I don't get what you are saying here. For me there's an inherent difference between a fuction and a function pointer. You shouldn't try to conflate them in a system language in my opinion.
On any hardware, a function call will boil down to set up stuff on the stack and branching, jumping, calling or whatever on the ADDRESS of the function's code. In other term, what is generated always involve the function's instruction address. If you dereference that address, you get instructions, but that hardly something useful to have at higher levels because you can"t do anything with it anyway.
It's the same with an int on the stack, isn't it (literally on the stack; not optimized into a register). To access it, one has to dereference its address but you wouldn't like conflating int and int*. The difference is between the address being a constant (maybe relative to the stack) and being totally unknown. In my opinion this is a big difference, also for functions. ...
This is basically what is done for objects, and with great benefice.
Aug 07 2012
prev sibling next sibling parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
 To me, the first big failure of D to implement functional style 
 is to not have first class functions. You get a function using 
 & operator. But does it really make sense ? See code below :

 void foo(){}
 void bar(void function() buzz) {}

 void main() { bar(foo); } // This will execute foo, and so 
 fail. Functions are not first class objects.

 void main() {
     auto bar = &foo;
     foo(); // Do something.
     bar(); // Do the same thing.
     auto buzz = &bar;
     (*buzz)(); // Do the same thing.
 }

 Functions don't behave the same way is they are variables or 
 declared in the source code.

 Worse, foo was before a function call. Now it isn't anymore. 
 foo, as a expression have a different meaning depending on what 
 is done on it. It would become very confusing if foo return a 
 reference, so it is an lvalue and & is a valid operation on the 
 function call.

 As D don't enforce purity like functional programing does, it 
 can't be up to the compiler to decide when does the function 
 get executed.

 Then come UFCS. UFCS allow for function calls with parameters. 
 It is still inconsistent.

 void foo(T)(T t) {}

 a.foo; // foo is called with a as argument.
 &a.foo; // error : not an lvalue

 Now let imagine that foo is a member function of a, &a.foo 
 become a delegate. a.foo is still a function call. This is 
 still quite inconsistent.

 Implementing all this is almost impossible when you add 
  property into the already messy situation. Additionally, the 
 current implement fails to provide the basics of functional 
 programing, and break several abstraction provided by other 
 languages features. C++ has proven that bad association of good 
 language features lead to serious problems.

 This require to be formalized in some way and not based on 
 dmd's implementation. Inevitably, the process will lead to code 
 breakage (adding or removing some ()/&).

 Reading the  property thread, it seems that most people want to 
 keep dmd's current behavior. Because code rely on it. This make 
 sense, but if dmd's implement is the only goal, it means that 
 other compiler are only to be reverse engineering dmd's 
 behavior, and are guaranteed to lag behind. Considering this, I 
 seriously wonder if it make sense to even try to follow dmd's 
 behavior and decide whatever seems the right one when writing a 
 D compiler, which will result in community split, or no 
 alternative compiler produced for D.

 I also have some proposal to fix thing, even some that would 
 allow a.map!(...).array() to still be available. But 
 inevitably, some other construct will broke. At this point, 
 what matter isn't really what solution is adopted, but do we 
 still want to be dependent on dmd implementation for D features.
I'm not sure if I understand your point perfectly, but I definitely feel that the way D handles optional parens is awful. The other day I noticed that the following is a syntax error (DMD 2.059): class A { void B() {} } auto a = new A().B(); // ^ semicolon expected following auto declaration, not '.' Even without silly errors like this, optional parenthesis create ambiguities, and ambiguities are bad. Maybe there is a sane way for parenthesis to be optional, but the way I've seen D behaving is *bizarre*. The compiler should *expect* parenthesis, and only assume that the parenthesis are missing if it's the only way to compile without an immediate error. So for example, - if foo is a non- property function that returns another function, foo() must invoke foo itself and never the function that foo returns. - if I say "&foo" where foo is a non- property function, it should always take the address of the function, never take the address of the return value. - The rules shouldn't change if you replace "foo" with a complex expression like "x.y[z]" or "new Module.ClassName".
Aug 06 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2012 08:05 PM, David Piepgrass wrote:
  ...
 I'm not sure if I understand your point perfectly, but I definitely feel
 that the way D handles optional parens is awful. The other day I noticed
 that the following is a syntax error (DMD 2.059):

 class A { void B() {} }
 auto a = new A().B();
              // ^ semicolon expected following auto declaration, not '.'
Obviously. No clue what this snippet is trying to do.
 Even without silly errors like this, optional parenthesis create
 ambiguities, and ambiguities are bad. Maybe there is a sane way for
 parenthesis to be optional, but the way I've seen D behaving is *bizarre*.
Examples?
 The compiler should *expect* parenthesis, and only assume that the
 parenthesis are missing if it's the only way to compile without an
 immediate error. So for example,
 - if foo is a non- property function that returns another function,
 foo() must invoke foo itself and never the function that foo returns.
This is the case.
 - if I say "&foo" where foo is a non- property function, it should
 always take the address of the function, never take the address of the
 return value.
This is the case.
 - The rules shouldn't change if you replace "foo" with a complex
 expression like "x.y[z]" or "new Module.ClassName".
If I get this right then this is the case. If there are any deviations from these rules then they should be filed as compiler bugs.
Aug 06 2012
parent reply "David Piepgrass" <qwertie256 gmail.com> writes:
 class A { void B() {} }
 auto a = new A().B();
 // ^ semicolon expected following auto declaration, not '.'
Obviously. No clue what this snippet is trying to do.
Well I meant "int B() { return 0; }" of course. I think you deliberately miss the point.
Aug 06 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 07/08/2012 07:45, David Piepgrass a écrit :
 class A { void B() {} }
 auto a = new A().B();
 // ^ semicolon expected following auto declaration, not '.'
Obviously. No clue what this snippet is trying to do.
Well I meant "int B() { return 0; }" of course. I think you deliberately miss the point.
According to what is described here : http://dlang.org/expression.html this is legal and is a dmd bug.
Aug 07 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 20:05, David Piepgrass a écrit :
 I'm not sure if I understand your point perfectly, but I definitely feel
 that the way D handles optional parens is awful.
That is the point. More specifically, the fact that D picks functionality from different paradigm here that totally don't integrate with each other. - Like in functional, function are called without (). - Purity isn't enforced, so the () is actually important so . . . - Function cannot be first class without ambiguities. So they aren't. - A first class function exists, and the behavior is different than other functions. - Member methods have semantic similar to declared ones. - But UFCS don't. They also don't have the behavior of first class object cited above. - UFCS include it's own layer of mess. - property does the same. - Most of this isn't defined anywhere, except in dmd source code. - dmd source code is known to have bugs on the very point, so no spec exists at all. Not even documents describing the intent that can clarify what is a bug and what is feature. So what ? So it is unlikely that any other tool will have semantic identical to dmd's. At this point, I'm questioning if any tool should even try.
Aug 07 2012
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2012 06:40 PM, deadalnix wrote:
 It is known that some part of D are very dmd's implementation defined.
 One of the part is manipulation function as first class objects and
 implicitly calling them.

 Some digressions about functional programing first.

 Functional programming emphasis function purity,  function as first class
 objects and immutability (among other things). Now, function is data
 (and can be passed as first class object to other function for instance)
 but no difference exists anymore between data and function.

 A function which is pure, with no parameters, will always return the
 same value. The difference between what is a function without argument
 and what is a constant is up to the compiler.

 The same way, the difference between a delegate and an expression don't
 exist anymore.

 This allow functional style to remove explicit call of functions. In
 fact, when does the function get called is irrelevant, because they are
 pure. This is for instance THE thing is haskell.

 The difference between a function call and a delegate creation is
 blurred, don't exists anymore and don't matter anymore regarding program
 result.

 D intend to implement functional, and get an mix of all the feature
 cited above, but in a way that don't work properly and is mostly dmd's
 implementation defined.

 Let's put aside the  property phenomena, this isn't that important here.

 To me, the first big failure of D to implement functional style is to
 not have first class functions.
Last time I checked, D still had closures. The 'first big failure of D to implement functional style' is the lack of most of the other features traditionally encountered in functional languages.
 You get a function using & operator.  But
 does it really make sense ? See code below :

 void foo(){}
 void bar(void function() buzz) {}

 void main() { bar(foo); } // This will execute foo, and so fail.
 Functions are not first class objects.
Actually it is just that function declarations don't introduce symbols that are bound to values of the first class type. Try enum foo = (){};
 void main() {
      auto bar = &foo;
      foo(); // Do something.
      bar(); // Do the same thing.
      auto buzz = &bar;
      (*buzz)(); // Do the same thing.
 }

 Functions don't behave the same way is they are variables or declared in
 the source code.
Which is by design and has both obvious benefits and obvious drawbacks.
 Then come UFCS. UFCS allow for function calls with parameters. It is
 still inconsistent.

 void foo(T)(T t) {}

 a.foo; // foo is called with a as argument.
 &a.foo; // error : not an lvalue

 Now let imagine that foo is a member function of a, &a.foo become a
 delegate. a.foo is still a function call. This is still quite inconsistent.
&a.foo could be disallowed if foo is bound by UFCS.
 Implementing all this is almost impossible when you add  property into
 the already messy situation.  Additionally, the current implement fails
 to provide the basics of functional programing, and break several
 abstraction provided by other languages features. C++ has proven that
 bad association of good language features lead to serious problems.

 This require to be formalized in some way and not based on dmd's
 implementation. Inevitably, the process will lead to code breakage
 (adding or removing some ()/&).
The formalisation can formalise the behaviour in a compatible or mostly compatible way. eg: a symbol that refers to a function (template) declaration can appear in some distinct contexts: 1. its address is taken 2. it is called 3. it is assigned to 4. none of the above In case 1: - If the function was looked up by UFCS, then this is the problematic case. Eg. just error out. - the address of expression will evaluate to a function pointer if the function is static and to a suitable delegate otherwise. In case 2: - Call. In case 3: - Call with the assigned expression. In case 4: - Rewrite to a call without parameters. Why would this be hard to implement?
 Reading the  property thread, it seems that most people want to keep
 dmd's current behavior. Because code rely on it. This make sense, but if
 dmd's implement is the only goal, it means that other compiler are only
 to be reverse engineering dmd's behavior, and are guaranteed to lag
 behind. Considering this, I seriously wonder if it make sense to even
 try to follow dmd's behavior and decide whatever seems the right one
 when writing a D compiler, which will result in community split, or no
 alternative compiler produced for D.
I think this is blown out of proportion.
 I also have some proposal to fix thing, even some that would allow
 a.map!(...).array() to still be available. But inevitably, some other
 construct will broke. At this point, what matter isn't really what
 solution is adopted, but do we still want to be dependent on dmd
 implementation for D features.
That is inevitable if there is only one front end implementation and no formal specification.
Aug 06 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 06/08/2012 20:15, Timon Gehr a écrit :
 To me, the first big failure of D to implement functional style is to
 not have first class functions.
Last time I checked, D still had closures. The 'first big failure of D to implement functional style' is the lack of most of the other features traditionally encountered in functional languages.
I mentioned first class function. Functions are not first class, you have to go throw & .
 Actually it is just that function declarations don't introduce symbols
 that are bound to values of the first class type.

 Try enum foo = (){};
That is what I mean when I say that function are not first class.
 &a.foo could be disallowed if foo is bound by UFCS.
It wouldn't be more consistent and would limit expressiveness.
 The formalisation can formalise the behaviour in a compatible or mostly
 compatible way. eg:

 a symbol that refers to a function (template) declaration can appear in
 some distinct contexts:

 1. its address is taken
 2. it is called
 3. it is assigned to
 4. none of the above

 In case 1:
 - If the function was looked up by UFCS, then this is the problematic
 case. Eg. just error out.
 - the address of expression will evaluate to a function pointer
 if the function is static and to a suitable delegate otherwise.

 In case 2:
 - Call.

 In case 3:
 - Call with the assigned expression.

 In case 4:
 - Rewrite to a call without parameters.

 Why would this be hard to implement?
Well it isn't straightforward, add property into that and it become quite scary, now try to keep up with dmd that introduce changes into that process every release or so and you'll get my point.
 Reading the  property thread, it seems that most people want to keep
 dmd's current behavior. Because code rely on it. This make sense, but if
 dmd's implement is the only goal, it means that other compiler are only
 to be reverse engineering dmd's behavior, and are guaranteed to lag
 behind. Considering this, I seriously wonder if it make sense to even
 try to follow dmd's behavior and decide whatever seems the right one
 when writing a D compiler, which will result in community split, or no
 alternative compiler produced for D.
I think this is blown out of proportion.
As you say :
 That is inevitable if there is only one front end implementation and no
 formal specification.
The function call mess is one aspect.
Aug 06 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2012 09:33 PM, deadalnix wrote:
 Le 06/08/2012 20:15, Timon Gehr a écrit :
 To me, the first big failure of D to implement functional style is to
 not have first class functions.
Last time I checked, D still had closures. The 'first big failure of D to implement functional style' is the lack of most of the other features traditionally encountered in functional languages.
I mentioned first class function. Functions are not first class, you have to go throw & .
 Actually it is just that function declarations don't introduce symbols
 that are bound to values of the first class type.

 Try enum foo = (){};
That is what I mean when I say that function are not first class.
What I mean when I say that functions are not first class is that there are no first class functions. If functions are not first class there eg. is no built-in way to pass a function to another function or lexical closure does not work. Basing the decision on whether '&' is required in some places is not useful.
 &a.foo could be disallowed if foo is bound by UFCS.
It wouldn't be more consistent and would limit expressiveness.
It can as well be allowed, because fields/properties of 'a' can behave in a similar way. I don't really see what kind of consistency you aim to preserve though. This consistency argument feels to me like complaining about the fact that &a.method returns a delegate while &a.field returns a pointer and &a.property fails with a not an lvalue error.
 The formalisation can formalise the behaviour in a compatible or mostly
 compatible way. eg:
 ...
 Why would this be hard to implement?
Well it isn't straightforward, add property into that and it become quite scary,
It is a bunch of syntactic rewrite rules. How hard can it be?
 now try to keep up with dmd that introduce changes into
 that process every release or so
I don't expect that to be the case.
 and you'll get my point.
What is this point? This feature is quite small and should be easy to add even after almost all of the analysis implementation is finished without spending much attention to it.
Aug 06 2012
prev sibling parent reply "Dejan Lekic" <dejan.lekic gmail.com> writes:
You do not seriously expect D to copy exactly how Haskel (or any 
other similar declarative langauge) treat functions? Does it 
really have to be an exact copy? I am not trying to defend D 
language designer(s) here, just trying to say that D should have 
own style, if there is sense behind it, it does not necessarily 
have to conform 100% to any paradigm.

The main reason for me to start using D 10 years ago was the 
obvious PRAGMATIC design of the language. I did not start using 
it because of (possible) functional orientation. Actually, I 
would probably turn my back on D if I saw it promotes declarative 
style more than imperative.

I think the current mix of both worlds is the best direction for 
D.
Aug 07 2012
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 07/08/2012 17:14, Dejan Lekic a écrit :
 You do not seriously expect D to copy exactly how Haskel (or any other
 similar declarative langauge) treat functions? Does it really have to be
 an exact copy? I am not trying to defend D language designer(s) here,
 just trying to say that D should have own style, if there is sense
 behind it, it does not necessarily have to conform 100% to any paradigm.

 The main reason for me to start using D 10 years ago was the obvious
 PRAGMATIC design of the language. I did not start using it because of
 (possible) functional orientation. Actually, I would probably turn my
 back on D if I saw it promotes declarative style more than imperative.

 I think the current mix of both worlds is the best direction for D.
The problem isn't about following haskell precisely or not (I think we shouldn't). The problem is wanting to have everything, and resulting in getting nothing. Let's take haskell as example. Function are all pure. So it doesn't matter when a function get executed or not, and, as a result, haskell don't need a explicit function call like () in D. Some people find that great, and want it to be the case in D. So D drop () usage. Now, as D don't enforce purity, when does the function get executed is important. As a result, complicated scheme is implemented to know when does the function get executed, wand when it doesn't (You'll notice *4* families of scheme for that in D). As a result, the design is overly complex, and defined nowhere. Just to have that haskell feature, that work well in haskell because of some other properties of the language D don't have.
Aug 07 2012
next sibling parent "David Piepgrass" <qwertie256 gmail.com> writes:
 The problem isn't about following haskell precisely or not (I 
 think we shouldn't). The problem is wanting to have everything, 
 and resulting in getting nothing.

 Let's take haskell as example. Function are all pure. So it 
 doesn't matter when a function get executed or not, and, as a 
 result, haskell don't need a explicit function call like () in 
 D.

 Some people find that great, and want it to be the case in D. 
 So D drop () usage.

 Now, as D don't enforce purity, when does the function get 
 executed is important. As a result, complicated scheme is 
 implemented to know when does the function get executed, wand 
 when it doesn't (You'll notice *4* families of scheme for that 
 in D).

 As a result, the design is overly complex, and defined nowhere. 
 Just to have that haskell feature, that work well in haskell 
 because of some other properties of the language D don't have.
What are the 4 "families of scheme to know when does the function get executed"?
Aug 08 2012
prev sibling parent "Graham Fawcett" <fawcett uwindsor.ca> writes:
On Tuesday, 7 August 2012 at 18:36:28 UTC, deadalnix wrote:
 The problem isn't about following haskell precisely or not (I 
 think
 we shouldn't). The problem is wanting to have everything, and
 resulting in getting nothing.

 Let's take haskell as example. Function are all pure. So it 
 doesn't
 matter when a function get executed or not, and, as a result,
 haskell don't need a explicit function call like () in D.
That's not a good example. Haskell may not use parentheses for function parameters, but that has nothing to do with purity, or even with non-strict evaluation. It's because, syntactically, concatenation in Haskell represents function application. The Haskell expression: f x y z is equivalent to the Haskell expression: (((f x) y) z) translated into D syntax: ((f(x))(y))(z), or just f(x)(y)(z). Graham
Aug 08 2012
prev sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Tuesday, 7 August 2012 at 15:14:09 UTC, Dejan Lekic wrote:
 You do not seriously expect D to copy exactly how Haskel (or 
 any other similar declarative langauge) treat functions? Does 
 it really have to be an exact copy? I am not trying to defend D 
 language designer(s) here, just trying to say that D should 
 have own style, if there is sense behind it, it does not 
 necessarily have to conform 100% to any paradigm.

 The main reason for me to start using D 10 years ago was the 
 obvious PRAGMATIC design of the language. I did not start using 
 it because of (possible) functional orientation. Actually, I 
 would probably turn my back on D if I saw it promotes 
 declarative style more than imperative.

 I think the current mix of both worlds is the best direction 
 for D.
I'm not arguing, but going to give my two or three cents on the matter; I can't recall if someone already mentioned all this. Other than 0-1 arguments being allowed to operate with or without parentheses, functionally is there any real reason to disallow or force it? Aside from variables being unable to use ()'s there seems to be no real difference otherwise. In some respects that's a good thing, but you can also take a built-in type and seamlessly replace it with functions or properties depending on the needs; The only difference is certain options (like .init or .min/.max) no longer are accessible. D can be a terse language, and building up from other language's mistakes and faults to make something newer, more robust, more useful. So what if there might be 3 different ways to call something, that doesn't make any one of them wrong compared to another. One way of calling it may be simpler or better than another, and having parentheses unnecessarily (be it property or otherwise) seems silly. assuming definition: string strip(string input); string x = "something"; auto y1 = x.strip; auto y2 = x.strip(); auto y3 = strip(x); All do the same thing. Since strip isn't a property, more restrictive may prevent y1 from working. But if you want to chain commands, the ()'s just seem ugly and don't help much. So what if it's a function? assuming definition: string replace(string input, string from, string to); auto y1 = x.strip.replace("from", "to"); auto y2 = x.strip().replace("from", "to"); auto y3 = strip(x).replace("from", "to")); auto y4 = replace(x.strip, "from", "to")); auto y5 = replace(x.strip(), "from", "to")); If replace was a template, perhaps: assuming definition: string replace(string from)(string input, string to); auto y1 = x.strip.replace!"from"("to"); auto y2 = x.strip().replace!"from"("to"); auto y3 = strip(x.replace!"from"("to")); auto y4 = replace!"from"(x.strip, "to")); auto y5 = replace!"from"(x.strip(), "to")); us it's a function but we already sort of know that with the chaining. If properties are enforced with only one calling verbose than needed. Expressiveness is important, and if it feels most natural writing a certain way, or easier to follow then it should be allowed (assuming it has no impact on performance or language structure). I guess at this point, I'd almost suggest leaving it as it is; Open for the programmer, but a switch to make it more strict for say libraries, or where there's a potential for it to be a variable in some cases and something else (static if's for types).
Aug 07 2012