View mode: basic / threaded / horizontal-split · Log in · Help
August 06, 2012
Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
> 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".
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
August 06, 2012
Re: Functional programming in D and some reflexion on the () optionality.
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.
« First   ‹ Prev
1 2 3
Top | Discussion index | About this forum | D home