On 09/21/2011 10:20 PM, Andrei Alexandrescu wrote:
On 9/21/11 2:29 PM, Timon Gehr wrote:
On 09/21/2011 06:55 PM, Andrei Alexandrescu wrote:
I'm back from the Strange Loop conference. It's been an interesting
experience. The audience was very diverse, with interest in everything
functional (you'd only need to say "Haskell" or "monad" to get positive
aahs), Web/cloud/tablet stuff, concurrent and distributed systems.
Mentioning C++ non-mockingly was all but a faux pas; languages like
Scala, Haskell, Clojure, and Erlang got attention. The quality of talks
has been very variable, and unfortunately the
over-confident-but-clueless-speaker stereotype was well represented.
I gave a talk there
(https://thestrangeloop.com/sessions/generic-programming-galore-using-d)
which has enjoyed moderate audience and success. I uploaded the slides
at http://erdani.com/d/generic-programming-galore.pdf and the video may
be available soon.
I am looking forward to it!
There was a very strong interest in D's CTFE abilities, which we should
consider a strategic direction going forward.
One milestone would be to make DMD GC enabled. CTFE string manipulation
sucks up too much memory.
Also finalizing D's
concurrency language and library infrastructure is essential. These two
features are key differentiators. Also, capitalizing on D's functional
features (which are e.g. better than Scala's but less known) would add
good value.
Where D still loses when compared to Scala is functional code syntax:
val newcol = collection filter {x=>x<5} map {x=>2*x}
Yoda this wrote.
Actually it is the order in which it will (conceptually at least) be
executed =). D can do similar things on arrays with UFCS.
or maybe
val newcol = for(x<-collection if(x<5)) yield 2*x
Too baroque.
vs
auto newrange = filter!((x){return x<5;})(map!((x){return 2*x;})(range));
auto newrange = filter!"a<5"(map!"2*a"(range));
At first some people get the heebiejeebies when seeing string-based
lambda and the implicit naming convention for unary and binary
functions.
Yes, I have seen it happen multiple times. There are even people who
consider strings-as-code an evil concept entirely :o). How are CTFE and
string mixins best introduced to people who think they might be too lazy
to bother about learning more about the language?
More importantly, there's the disadvantage you can't access
local functions inside a string lambda.
I often want to do that. We could replace the 5 and 2 constants by stack
variables, and then the point would be made.
We should probably add a
specialized lambda syntax.
It would probably be a simple addition as it is restricted mostly to the
parser.
Like this? Or is there a better lambda syntax around?
int u,v;
auto newrange=map!(x=>u*x)(filter!(x=>v<x)(range));
auto newrange = range.filter!(x=>v<x).map!(x=>u*x);
I believe that is part of the reason why it is less known. If I write
functional style code in D I unfortunately usually get feedback that it
is "extremely hard to read". Furthermore, Scala has built-in tuples, and
eg delimited continuations, which enable some more functional
programming idioms. std.algorithm/std.range are also considerably (and
as I understand, deliberately) underpowered for FP when compared to eg
Haskell's standard set of libraries, and writing a good range from
scratch is usually a serious undertaking.
I wonder what Scala/Haskell idioms we should borrow for D.
In my opinion these are worth looking into:
Haskell: This might not happen, but having a tiny little function with a
literate name for most common tasks is very convenient. I think other
Haskell idioms not already in D are hard to translate because the type
system and execution model of the two languages are so different. I have
managed to use D for haskell-lookalike toy examples nonetheless. (it
leads to extremely obfuscated code because of many nested lambdas and a
bug in the lazy storage class that needs to be worked around)
Both: (Concise expression-based lambdas.) Pattern matching, if it can be
incorporated in a good way. (dynamic) lazy evaluation. Scala has lazy
val. It would play very nicely with Ds purity, but maybe suboptimally
with immutable (for immutable structures, the lazy fields would have to
be computed eagerly)
Generators? (those are implicit in Haskell) Something like this example
would automatically create a compile-time lazy range to compute a power
set efficiently.
auto powerset(T)(T[] x) {
foreach(i;0..1<<x.length) {
yield (size_t i){
while(i){
yield x[bsf(i)];
i &= i-1;
}
}(i);
}
}
Equivalent code that spells out the nested structs with all the
front/popFront/empty etc contains considerable boilerplate. I am not
sure how powerful it could be, but it should be possible to
automatically let the following example propagate random access iff fun
is pure, based on the observation that it does not have state:
auto map(alias fun, R)(R m){
foreach(x; m) yield unaryFun!fun(x);
}
The range concept is very important in D code, some syntactic sugar like
this would help a lot.
Another big advantage that Scala has is that it supports pattern
matching. It is usually about the first feature functional programmers
care about.
What do you think is the biggest advantage that Ds functional
programming has in comparison to Scala's?
1. True immutability
2. True purity
Compiler-enforced immutability and purity.
3. Pass by alias
It is extremely nice how templates are automatically instantiated in the
correct scope. The fact that delegates are constrained to one context
pointer and that static function literals don't decay to delegates tends
to be annoying though. (also, that there is no inference if a context
pointer is needed)
For a language that has so firmly branded itself as functional, Scala
has a surprisingly poor handling of immutability and referential
transparency aka purity. My understanding is that Scala's immutability
merely consists of final values and a web of conventions. (Please
correct me if I'm wrong.)
I think you are right. But in Scala, much of the language semantics are
actually implemented in the library. The immutable collections could be
considered truly immutable by some.