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