« Team Equilibrium | Main | Going to North Carolina... »

January 10, 2007

Comments

Isaac Gouy

"A first year CS student would have no trouble understanding the C++ example."

Because we taught them to understand imperative programs!

If we taught them to understand functional programs in the first year then we would expect them to have no trouble understanding the Haskell example.

"At Oxford, we use Oberon as the second language we teach to our undergraduate students (the first one is Haskell)."
http://spivey.oriel.ox.ac.uk/mike/obc/

Presumably those first year students would have not trouble understanding your Haskell example.

Michael Feathers

"Presumably those first year students would have not trouble understanding your Haskell example."

I agree. I think that it is hard to have a good handle on simplicity when we aren't conscious of what we are acclimated to. Foreign language text looks absolutely incomprehensible if you don't know the language, but clear and lucid if you do.

I've been playing with Haskell a bit lately, and liking it. I'm now going through the inevitable process of mapping it back to my OO experience. It feels odd.

Steven Stone

Preface: I like Haskel quite a lot, and my defense of Perl is not an attack thereon.

However: why make the Perl example artificially complex? If it were written as:

sub miles_to_kilometers { map {$_ * 1.61} @_ }

then it would look an awful lot more like the Haskel example (setting aside Perl's much-maligned punctuation, it still would highlite the ability for Haskel to omit explicit arguments, but show parity in other respects).

It's pedantic, I know, and in an example such as this, Haskel *should* still appear simpler (because it is). It's just a pet peeve that whenever two languages are compared by example, one example always tends to better-written and one worse-written, each in the context of its own language. Also, for that matter, example problems are generally *chosen* for the purpose of highliting one language over another.

If the example, for instance, had involved I/O, I doubt that Haskel would have had a chance at appearing "simple". Even by your standard of "simple -- once you understand it" (which I both like and dislike), monads fair poorly, relative to imperative I/O.

Michael Feathers

"However: why make the Perl example artificially complex?"

Apologies on that. I didn't write the examples (and it wouldn't have helped, I've learned and forgotten Perl three times so far in my life), I pulled the examples from the LtU post. I just thought there was an interesting point to make there and it's more about the abstractions than the language.

On another site, someone pointed out that the C++ example could be made a lot simpler with std::transform. I agree.

Isaac Gouy

"... hard to have a good handle on simplicity when we aren't conscious of what we are acclimated to."

Now if we can remember back decades, those who've done any high-school maths will already have used functions, and those who've done a little more high-school maths will already have used function composition.

In contrast, iterative indexed assignment is a wierd new concept.


"... now going through the inevitable process of mapping it back to my OO experience"

Of course you're using something that supports both functional and OO programming, something like Nice or Scala ;-)

Michael Feathers

"Of course you're using something that supports both functional and OO programming, something like Nice or Scala ;-)"

Nah, Haskell. Cold turkey. :-) I played with OCaml a few years ago, but I then decided it would be good to try a pure functional language.

Isaac Gouy

"it would be good to try a pure functional language"

Sure enough, but how are you going to 'map it back to your OO experience' without having a straightforward way to do both fp and oop?

I found having both oop and fp in Mozart/Oz really simplified re-writing programs from one style to another, and I think using oop and fp in Scala would also provide an easy way to switch from one style to another, without also having to mentally adjust syntax.

June Kim

Thanks for the nice post, and I agree to the comment that said a particular implementation is more easily understood by freshmen due to the students' historical reasons; the perception between low & high simplicity would be significantly affected by the cultural background and context of the person. However, as Christopher Alexander explores in his Nature of Order series, I think there could be some portion of absolute beauty, and simplicity that is universal, considering we are physically more or less same.

Oh, BTW, J (http://jsoftware.com) is even simpler than Haskell's.

m2k=.*&1.61

In J's world it happens very often that the name(identifier) is longer than the referent(code). So the referent becomes a name itself(idiom).

+/%# is the code for average.

Jonathan

While I know that this doesnt really have a lot to do with your point, the first example you posted was in C, not in C++.

The first example in C++.

void miles_to_kilometers(const vector &mile_list, vector &kilo_list_out)
{
transform(mile_list.begin(), mile_list.end(), back_inserter(kilo_list_out), bind1st(multiplies(), 1.61));
}

Ben W.

FYI, between the perl and haskell versions, lies the ruby version:

def miles_to_kilometers(miles)
miles.map {|mile| mile * 1.61}
end

which is IMO, much less "magical" than the perl equivalent, e.g. explicit params instead of $_

Oh, and we could use the "pre" tag in the comments, btw...

Ziggy Stardust

Your C++ contains a memory leak. Not that important for the purpose of this article, but it is a bad example for those who do not know C++.

tim

I think you have an unnecessary 's' in your theme. I think you're talking about high 'implicity'. . The C++ example is pretty explicit, but it's also not very simple. The Haskell example at the end is very small, but requires you (and the runtime) to know things that aren't being said explicitly.

I think that it is simpler, too, in a hard measure of the number of operators, entities, side-effects and syntactic elements involved in the declaration. In fact, simplicity is pretty much the inverse of that count. maybe (operators + entities + syntax) * sideEffects. ;-)

It's just funny that part of the low count for haskell is that some parts of it are implicit and elsewhere.

I think that a moderate implicity and a high simplicity are good things, though I follow the old Python adage that explicit is better than implicit.

Michael Feathers

June: Thanks! I'll have to try J.

Jonathan: Fair enough, it could've used STL, but it's definitely C++ code. I like C++, but one of the things I've noticed traveling around is that there are far people working in C++ that looks like that than there are avid users of STL and Boost. I guess that long tail of history is success, in a way.

Ziggy: No memory leak. It's just a function written with the assumption that the caller will own the result. Note to self: stop defending the examples, Mike. You didn't even write them(!)

Tim: Yeah, I struggle with the implicitness in Haskell, but I wonder again how much of it is just what we're used to? Dynamically typed languages are inherently more implicit than manifest statically typed languages like Java and C++. Neat metric, btw. It would give most Haskell code a rank of zero. :-)

The comments to this entry are closed.