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

Timon Gehr timon.gehr at gmx.ch
Mon Aug 6 11:15:55 PDT 2012


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.


More information about the Digitalmars-d mailing list