retard wrote:
Mon, 21 Dec 2009 12:13:48 -0600, Andrei Alexandrescu wrote:

Daniel de Kok wrote:
On 2009-12-19 21:04:32 +0100, Andrei Alexandrescu
<seewebsiteforem...@erdani.org> said:
"This code is shown for its elegance rather than its efficiency. Using
++ in this way is not generally considered good programming practice."

So if the code is inefficient and in poor programming practice, how in
this blessed world could it count as elegant?
Well, it is elegant in that it is very readable, and as such provides
educational value.
I disagree. We don't want to educate people to write patently
inefficient code and in poor programming practice.

When I was doing Windows programming, I noticed that many example in the
documentation were written in terrible style. I also noticed that much
of the production code I was working with was often stitched from
documentation examples.

There are very many examples in programming text books that may not be
efficient, but do show the solution to a problem elegantly.
Yeah, exponential-time Fibonacci and bubble sort come to mind.

In my mind "elegant" and "at a polynomial blowup in complexity" can't be
reconciled anywhere outside a Turing machine introduction where
everything is equivalent within a polynomial difference in time and
space.

E.g. consider list reversal in a recursive function or predicate. It is
easy to see that to reverse a list, you append the head to the reversed
tail. E.g.:

reverse([],[]).
reverse([H|T],R) :-
 reverse(T,RT),
 append(RT,[H],R).

Of course, this is very inefficient, since the program is doing (slow)
appends all the time, and is not tail-recursive. You can do it a lot
faster with an accumulator, but it makes the solution also less
understandable, and not something you'd want to present
students/readers immediately:

reverse(List,Reverse) :-
 reverse_aux(List,Reverse,[]).

reverse_aux([],List,List).
reverse_aux([H|T],List,List0) :-
 reverse_aux(T,List,[H|List0]).
Yah, I understand. But what you're really saying is that reverse offers
either a simple implementation or an efficient implementation, but not
both. That's hardly furthering my belief that FP has a crushing
advantage over pretty much anything else.

In another thread, "retard" wrote in response to the comparison of the
real quicksort in C and Haskell:

"What is so brilliant? Referential transparency is broken unless single-
threadedness is forced through monadic computation or by some other
means (uniqueness types comes to mind)."

I think that's a roundabout way of saying that functional programming is
unable to express certain algorithms easily or at all.

I think one of the things that make good programming languages good is that they allow hiding the complexity behind abstractions. This is something I remember from the SICP lectures. This is the ideology behind pure functional and pure object oriented programming. Reliability is transitive - if the building blocks are good, you can't screw /those/ things on higher level. Things like referential transparency also give you truly modular isolation in this way. You can always find a loop hole in languages like D.

That all sounds nice and I agree with it, but friction is also transitive. Inefficiencies add when you build higher-level abstractions, and may force you at some point to choose between good abstraction and working program.

The beauty of D is that it has low or often zero friction in the way of creating abstractions. That is very difficult to achieve.

As far as loop holes go, D's answer is SafeD. We managed to pull it in an extremely short time, and it's in some ways a gambit, but I am confident we have made the right decision. Until somebody provides a soundness proof a la Featherweight Java, there is no ultimate proof that SafeD is in fact safe, but I have reasons to believe there are no principle reasons we can't, and if you do find loopholes please let us know. Java has had for years safety holes, and naysayers enjoyed claiming it will always have. Now it's common knowledge that Java is safe.

I think I've already read what you think about abstractions. After all, it's not really surprising why in "systems programming languages" like D one can more or less easily convert any high level expression into machine code in their head. On relatively low level it's benefical to always think in terms of machine code instructions, stack layouts, pointers etc.

But on higher level, performance is in many domains only a secondary goal. Reliability is the main goal. If there's an abstract data type such as a queue or a stack, you simply can't produce traditional stack overflows because the abstraction protects against that. The container code can be really ugly spaghetti internally - the abstraction works as a public interface.

I fail to see how in D you'd be hard pressed to think in terms of lower level abstractions.


Andrei

Reply via email to