Slashdot Mirror


OCaml vs. C++ for Dynamic Programming

jcr13 writes "OCaml is nearly as fast (or sometimes even faster) than C, right? At least according to the Computer Language Shootout [alternate] (OCaml supporters often point to these shootout results). My results on a real-world programming problem (optimizing a garden layout using dynamic programming) disagree. On one particular problem instance (a garden of size 7x3), my C++ implementation finished in 1 second, while the OCaml implementation was still running after 16 minutes. Bear in mind that my OCaml implementation was dramatically faster than my equivalent Haskell code. It seems that if you program using a functional style in OCaml (which I did, using map, filter, and other recursive structures in place of loops), it is quite slow. However, most of the shootout OCaml programs rely heavily on OCaml's imperative features (unlike Haskell, OCaml doesn't force you to be a functional purist). If you write OCaml code that is isomorphic to C code, it will be fast---what about if you use OCaml the way it was meant to be used?"

161 comments

  1. Uh huh by QuantumG · · Score: 1

    So the OCaml compiler is supposed to figure out that there's a dynamic programming solution to the problem you have specified using higher order operators? That's a great idea. Let me know when you work that out cause I'd love to be able to just write a specification, press a button and get an optimised program. If you do manage to make this though, be sure not to tell those bastards who pay our salaries, they'll probably think we're not working anymore.

    --
    How we know is more important than what we know.
    1. Re:Uh huh by crmartin · · Score: 1

      No, he's trying to do it explicitly. I'm not an ML guru, but I'm kinda suspicious that "generate states" is getting called exponentially many times.

      Have you tried running any tracing of the program?

    2. Re:Uh huh by Anonymous Coward · · Score: 0

      Well, I guess you never written a compiler for a functional language nor used a highly optimized one. With a term graph rewriting approach you can (maybe almost) get this. Ever tried Clean, which is also top-ranked at thelanguage shootout site? You get what is called "dynamic programming" at not much cost. Try to write a fibonacci function, one version with the table method (always remember the last sum) and one the greedy way (fib(n) = fib(n-1) + fib(n-2)).

      Compare the running times.

      No difference.

      How can this work? Quite easy. As there are not side-effects (probably changing some value you'd not expect) the compiler can optimize aggressively. In the above example, the compiler sees that fib(x) with the same x occurs more than once. In it's internal representation, all occurances of this are indeed of the same identity. Term graph rewriting is superior to term rewriting (which usually haskell implementation use) in that respect since it accounts for such multiple occurances and can optimize.

      For the case, that functional languages (or languages living this paradigm further) get a rennessance some day, I predict, they will outperform imperative languages with a little more expressiveness than C (which is a decorator of assembler). They simply have much more potential.

  2. Hmmm ... by crmartin · · Score: 4, Interesting

    That difference is so dramatic that I wonder if you made a mistake in your functional implementation? Or is there something specific about your dynamic program that makes trouble?

    Dynamic programming depends basically on memoization (not "memorization", before someone complains about my typo) which inherently means preserving some state. If you don't preserve state, it becomes a good old, likely exponential time, recursive program. Any chance your implementation is not memoizing?

    1. Re:Hmmm ... by S.+Traaken · · Score: 1

      Maybe rtfa? ("My results")

    2. Re:Hmmm ... by crmartin · · Score: 1

      When you grow up, you'll learn that if the timing differs by three orders of magnitude from expectations, your problem is algorithmic; you'll smell that before you even need to read the code.

      Shortly after that, you'll also learn that if your dynamic programming solution is running 960 times longer than expected, the odds are awfully good that you've manage to make an exponential implementation of what should be a poly-time solution.

    3. Re:Hmmm ... by yasth · · Score: 1

      Or maybe just a plain old infinite loop.

      I mean it was never claimed to have umm finished corectly. I mean if every time a complicated program (dynamic programming counts as complicated/non-trivial in my book at least) went off and didn't finish I assumed the language sucked, well I would be out programing languages real quick.

      --
      I'd do something interesting, but my server can't handle a slashdotting.
    4. Re:Hmmm ... by Macrobat · · Score: 1
      Any chance your implementation is not memoizing?

      Well, he said he was memoizing in the article. You can check his code here.

      --
      "Hardly used" will not fetch you a better price for your brain.
    5. Re:Hmmm ... by bhurt · · Score: 5, Insightful

      Having read the article and looked at your Ocaml code, you do have at least one problem in your implementation. You're using lists. Lists are not for random access and modification. To change the nth element of a list, you need to modify (and reallocate) n elements.

      Try using a map instead. I'll send you example Ocaml code in a day or two (I'm moving, so I don't have that much free time to fix other people's bugs that I'm not getting paid to fix). Note that this is true in Haskell as well as Ocaml. Haskell may be just a little bit better at hiding the problem with laziness- but it's still a problem!

      Now, for the brutal part of the response: that big of a difference in performance almost certain does mean you've messed something up in your implementation. But, instead of saying "Gee- I wonder what I screwed up?" you said "Gee- Ocaml and functional programming must just suck!" That I still fault you for.

    6. Re:Hmmm ... by crmartin · · Score: 2, Informative

      Look down a little farther ... I commented specifically on his implementation.

      But here's another hint: the fact that there's a fuinction that says "memoize" doesn't necessarily mean it works.

    7. Re:Hmmm ... by crmartin · · Score: 1

      I know he *said* it was memoizing. Hell, maybe it is ... just exponentially many times.

    8. Re:Hmmm ... by Anonymous Coward · · Score: 1, Informative

      How long does it take for one to learn that you should be humble enough to make sure the program you wrote even halts before publicly complaining and posting results?

      Furthermore, looking at the ml code shows some questionable practices. He reinvents the map function, which isn't nessecary and could cause relative slowdowns if the ocaml library version has been optimized. It shouldn't take 16 minutes, though. I don't have an ocaml compiler installed at the moment, so I can't really verify and delve into why his program is going so slowly.

      Certainly, his adventure is a testament to how difficult it can be to migrate from one language to another, if not a testatment to the poor performance of Caml.

    9. Re:Hmmm ... by crmartin · · Score: 1

      Of course. Should have seen this yesterday ... but it was late. Using lists means that every access --- insertion or lookup --- in the list requires n/2 operations, or O(n). Thus, even if it is "memoizing", the memoizing means accessing every previous computed value every time (asymptotically); this means, sure enough, that the "memoization" isn't really memoizing, and the move-generation thing is running exponentially long.

  3. A guess... by joto · · Score: 1
    My "shoot from the hip" guess would be that your ocaml program is not efficient because it's not running the same algorithm. The Haskell program turns out not so slow, because Haskell is lazy, and in this particular case it's a big win.

    That doesn't mean that ocaml is slow. It means that dynamic programming in a functional programming style in an eager programming language requires a lot of care, or perhaps should even be avoided. A result that wouldn't surprise me the slightest.

    1. Re:A guess... by jovlinger · · Score: 0

      it's not running the same algorithm

      A good observation.

      I'd recommend re-implementing the C++ algorithm in OCaml (it has classes and objects, after all), and then seeing if that is in the same ball park.

      Then you can compare that to your purely functional version.

    2. Re:A guess... by jovlinger · · Score: 0

      Ignore parent post please.

      I should have read linked article more closely.

  4. My realworld results differ by cthulhuology · · Score: 2, Interesting

    You know I've implemented some real world applications recently for a contract job, and the Ocaml applications are actually faster than the C++ equivalents using the STL. So you mileage may vary based on your problem set (or Ocamlfu as the case may be). As for how Ocaml is supposed to be programmed, there's a reason Ocaml supports imperative programming, because you should use the form that is most efficient for your problem. Some programs benefit from a functional approach (and it helps if you implement properly tailrecursive functions, and make intelligent use of arrays and other block data structures). So one can argue Ocaml is not particularly functional, because it more pragmatically allows for multiple styles of programming. You can do functional programming in C++ actually, but depending on the optimizer you end up with stack issues. My experience with maintaining and extending the Ocaml programs over large C++ code bases is a world of difference. Ocaml wins hands down. Even extending the language to support 3rd party libraries, doesn't place sufficient barriers to maintenance. But ymmv... as with all things.

    1. Re:My realworld results differ by Cthefuture · · Score: 1

      Show me one example where O'Caml beats C++.

      I mean this seriously. I'm both an experienced O'Caml (and SML, Erlang) programmer and C/C++ programmer. I have never, ever, had anyone present a problem that can't be done faster in C or C++. You might have to solve the problem differently, but C/C++ wins every time as far as performance goes.

      Now implementation speed and/or maintenance is something different. O'Caml has a horrible syntax though. Regular SML is much better. They should've just stuck with that.

      --
      The ratio of people to cake is too big
    2. Re:My realworld results differ by snorklewacker · · Score: 5, Insightful

      Ocaml doesn't support any ad-hoc polymorphism (overloading) whatsoever in functions. Methods on the other hand can be overloaded, but not generic. This sort of thing makes it weaker than even C++ for generic programming, let alone Haskell, though I must admit not having to use template syntax makes me want to claw my eyes out a good deal less when reading it (or my hair when writing). Modules simply don't do it for me. Having to differentiate between HashTable.insert and SkipList.insert sort of defeats the purpose of abstract types, because no one thought to make module signatures themselves first-class (except Alice).

      Haskell type families are just elegance and beauty itself, but doing state in Haskell is an exercise in raw tedium. Very localized state (in one function) is easy enough, but anything more pervasive and you soon become more familiar with monads than you ever wanted to be. If you want a haskell program that doesn't suck up more memory than emacs, you have to stay away from many modern features so your program will compile with nhc98.

      Ocaml isn't seeing a lot of new work going into it -- the language definition seems to have become cast in stone. Haskell is always evolving, though typically in ways that are really impenetrable to those of us without PhD's in category theory and denotational semantics.

      I guess I could search the world over for my holy grail FP language, and always be dissatisfied...

      --
      I am no longer wasting my time with slashdot
    3. Re:My realworld results differ by Anonymous Coward · · Score: 0

      Cement (or concrete) is cast. Bronze is cast. Plaster is cast.

      Stone is carved or chiseled.

      And horses champ at the bit, they chomp apples.

    4. Re:My realworld results differ by angst_ridden_hipster · · Score: 3, Insightful
      Stone is carved or chiseled.

      Not always, not always.

      For example, I have seen two different kinds of tree castings made of stone: one, a negative casting made by molten lava that built up as an accretion on a tree (which obviously burned out), and two, a positive casting made through a slow fossilization ("petrification") process.

      I would happily come up with a false etymology originating in the parlance of lime-slakers, medieval wall builders, sarcophagus fillers, or even potters discussing cone-10 firing, but you'd probably call me on it.

      That being said, it is a weird phrase, that probably belongs with "mute points" and exclaimations like "here here!"

      --
      Eloi, Eloi, lema sabachtani?
      www.fogbound.net
    5. Re:My realworld results differ by Anonymous Coward · · Score: 0

      O'Caml has a horrible syntax though. Regular SML is much better. They should've just stuck with that.

      Stuck with what? OCaml and SML were developed at the same time from the same root. They just developed in different directions.

    6. Re:My realworld results differ by Anonymous Coward · · Score: 0

      SML is the direct decedent of ML. CAML was an off-shoot project done by INRIA which turned into OCAML.

      Hence the term "Standard ML" versus everything else.

    7. Re:My realworld results differ by Anonymous Coward · · Score: 0
    8. Re:My realworld results differ by Anonymous Coward · · Score: 0

      "moot point". You can't win. :-)

    9. Re:My realworld results differ by Anonymous Coward · · Score: 0

      Because using any syntax other than the normal is non-standard and just makes it harder on others trying to use or reuse your code.

      Variable syntax is not a good thing.

    10. Re:My realworld results differ by Anonymous Coward · · Score: 0

      I'm a bit surprised about your description of ocaml as not seeing much new work going into it. Actually we often get the opposite criticism from the Standard ML community: ocaml is such a moving target that you cannot write for it :-)
      The truth is somewhere in between: almost all releases of ocaml actually add or improve some features, but has to be very careful about not breaking existing programs. That's the trouble with being used for serious work...
      Also I don't understand your comment about how writing module names defeats the purpose of abstract types. These are quite independent notions. If you want to be able to freely change the kind of collection you use, you can parameterize your code as a functor. If you want the names to go away, nothing prevents you from wrapping an abstract type into an object, or wrapping your whole code into a functor.
      By the way, the original message is really a troll: if memoization matters, you have to be careful about how you do it. Comparing array accesses (indexed by integers) to searching through an association list, with the key itself a verbose description of the state, makes no sense at all.

    11. Re:My realworld results differ by ijones · · Score: 5, Informative

      It's not actually the case that Haskell "forces" functional purity, at least not in the way the submitter seems to think. You can do things that are a LOT like non-pure functions, you just have to use Monads. You have the so-called "unsafe" functions, which perform side-effects in otherwise "pure" functions.

      So you might ask, "If you're going to write code like that in Haskell, why not just use C++." The answer is because even when using Haskell in a non-idiomatic way, Haskell is still more beautiful :)

      Monads are a means of threading "stateful" code in a very clean and predictable way through your programs. The parent's comment, "Very localized state (in one function) is easy enough, but anything more pervasive and you soon become more familiar with monads than you ever wanted to be," is sorta like saying, "You can write high-level code in C++, but you will soon become more familiar with objects than you ever wanted to be."

      They are indeed a part of the language, and definitely a new concept, but monads aren't nearly as confusing as people seem to think, certainly not more confusing than objects, it's just a reputation issue that makes people think monads are confusing. Take it from a random-joe hacker like me. You don't need a PhD to perform IO in Haskell.

      For instance, here's a basic implementation of 'cat' in Haskell:

      import System.Environment(getArgs)

      main = do
      {a <- getArgs;
      lines <- mapM readFile a;
      putStr (concat lines);}

      The code:

      a <- b
      is similar to assignment.

      getArgs just reads in the command-line arguments as a list, so 'a' represents a list of the filenames.

      readFile takes a file name, reads the contents, and returns it as a list of lines.

      mapM means 'perform this computation once for each item in this list'

      putStr is obvious, concat just takes a list of lists and turns it into a single list.

      There's a paper, Tackling the awkward squad: monadic input/output, concurrency, exceptions, and foreign-language calls in Haskell about how to do these kinds of "real-world" things in Haskell.

      There's also a very cool version control system called darcs that's written in Haskell, and recently an implementation of Perl 6 called Pugs in Haskell.

      peace,

      isaac

    12. Re:My realworld results differ by Anonymous Coward · · Score: 0
      Show me one example where O'Caml beats C++.
      Is a factor of 100 enough for you?
    13. Re:My realworld results differ by Anonymous Coward · · Score: 0
      Methods on the other hand can be overloaded, but not generic.
      What do you mean ? There are polymorphic methods in OCaml.

      Having to differentiate between HashTable.insert and SkipList.insert sort of defeats the purpose of abstract type
      Why ? If you need to write code that abstract from the implementation and type of the data structure, module and functors are probably the way to go.

    14. Re:My realworld results differ by Anonymous Coward · · Score: 0

      Ocaml doesn't support any ad-hoc polymorphism (overloading) whatsoever in functions...
      Ocaml isn't seeing a lot of new work going into it -- the language definition seems to have become cast in stone...

      Heard of GCaml? The currently ongoing project to add ad-hoc polymorphism to OCaml? Hmm, doesn't sound like a fossilized language to me, but of course you may have a different definition of "nothing new".

    15. Re:My realworld results differ by jdowland · · Score: 1

      Show me one example where O'Caml beats C++.

      ICFP contests:

      2004 (ocaml 1st); 2003 (ocaml 1st); 2000 (ocaml 3rd, C++ 5th); >2001 (ocaml tied 3rd place with C);

      Others winners: 1998 C derivative, 2002 python (I think C++ actually beat OCaml this year)

    16. Re:My realworld results differ by Cthefuture · · Score: 1

      Beats C++ in performance. Those have nothing to do with speed.

      --
      The ratio of people to cake is too big
    17. Re:My realworld results differ by Anonymous Coward · · Score: 0

      What does that have to do with anything? The C++ version was not implemented correctly for C++. If implemented in a C++ way, it would be faster than OCaml.

    18. Re:My realworld results differ by Anonymous Coward · · Score: 0

      Actually mute point is a Britishism, it was fixed by the spelling reformers thank god.

    19. Re:My realworld results differ by arkanes · · Score: 1
      Cast stone is actually a real thing, a method of casting using concrete. Apparently it dates back almost 1000 years, so it's certainly old enough to be the origin of "cast in stone". However, my understanding of the origins of the term are that it had to do with the final version of a mold for a bronze statue, with design versions being done in clay.


      Oh, and it's "moot point" (no idea where the word moot comes from) and "hear, hear" (as in "we hear you". Or "preach it!").

    20. Re:My realworld results differ by bhurt · · Score: 1
      You said:
      Having to differentiate between HashTable.insert and SkipList.insert sort of defeats the purpose of abstract types, because no one thought to make module signatures themselves first-class (except Alice).


      I've never seen a real good reason this is usefull, myself (and yes, Virginia, I have done signifigant programming in C++ and other languages with this ability). Many data structures have the same or similiar implementations, but generally different performance characteristics. When I use a particular data structure, it's because I want those performance characteristics, and not others. Replacing the data structure I choose with another with different performance characteristics is likely to cause performance degredation, possibly severe performance degredation- like converting an O(N) algorithm into an O(N^2) algorithm.

      The classic example of this is lists and arrays. Both data structures have a "get the i-th element" operation, and both can have a "prepend a element" operation. With arrays, the ith operation is O(1), but the prepend operation is O(N) (I have to move all elements of the list out of the way to make room for the new head element). With lists, the ith operation is O(N) (I have to walk down the list), but the prepend operation is O(1). If my algorithm is heavily dependent up getting the ith elemenet, and never or almost never prepends an element, then I want to use arrays and stay far away from lists. If my algorithm prepends elements a lot, and never or rarely, goes and gets the ith element, then I want lists and not arrays. This is exactly the problem the original poster fell into (well, one of them anyways).

      Generic implementations of algorithms? Rare, in my experience. Rare to the point that duplicating the code is a legitimate alternative. I mean, consider sorting both lists and arrays. OK, with a little bit of trickery, you could probably write a bubble sort that was (more or less) equally efficient on both lists and arrays. But anything more complicated? OK, let's just limit it to linked lists. For singly linked lists, you basically need to do merge sort. But for doubly linked lists, I've written a variant of heap sort that is twice as fast as merge sort (you treat the next and previous pointers as left and right child pointers, convert the list into a true pointer based heap, and run heap sort on the result).

      It's a trade off. I don't see what Ocaml traded off as that valuable- and what Ocaml got in return is strong type checking and type inference. All the benefits of a real type system without the bondage and discipline aspects. All languages are trade offs. If you hold out for having your cake and eating it too, then yes, you will always be dissatisfied.
    21. Re:My realworld results differ by snorklewacker · · Score: 1

      > Generic implementations of algorithms? Rare, in my experience.

      Sincerely delusional to call it rare. It's an entire paradigm of programming. For example, you cannot generically write a functor that maps over any container structure if the algorithm needs to be aware of every possible type of container. Criminy, those were off the top of my head. How about Socket.read and Pipe.read? (I'm not familiar enough with the standard library). Heck, if python, perl, and even C have polymorphic behavior there, why the hell shouldn't a functional language?

      Mind you, Ocaml does have this polymorphism if you use objects, but for some reason, the O in Ocaml gets short shrift in its library.

      > It's a trade off. I don't see what Ocaml traded off as that valuable- and what Ocaml got in return is strong type checking and type inference.

      Haskell does it with type families and loses nothing. What they traded off is separate compilation, but it doesn't make it impossible, just difficult. I just can't believe I'm hearing someone argue against polymorphism at such length.

      --
      I am no longer wasting my time with slashdot
    22. Re:My realworld results differ by snorklewacker · · Score: 1

      Let's lose the weird justifications ... I just misspoke and meant to say "carved in stone". Though petrification sounds like a more accurate description of the process for ocaml.

      Why is it that I have a hankering for grits now, and want to watch Natalie Portman movies? Strange, that...

      --
      I am no longer wasting my time with slashdot
    23. Re:My realworld results differ by Anonymous Coward · · Score: 0

      As you pointed out, Haskell doesn't support operator overloading, either, so be careful in what you are asking for--people may get the impression that you'd much prefer that to type classes, which are, I think, somewhat better (you seem to agree).

      Anyway, I am afraid I don't know OCaml that well, but at a glance, this research looks pretty interesting (you might want to start with his Thesis).

      As for your comments about Haskell, first, though you say Haskell is always evolving and OCaml is set in stone, I believe you have that a bit backwards. Haskell98 is officially frozen; while development on the language continues, it is all non-standard, so generally compiler-specific (e.g. the ghc extensions, etc). OCaml appears to be officially still in development, though, as I said before, I don't follow it enough to really know what that means.

      And second, you comment that monads are tedius (I assume this is what you mean), and you are somewhat right. But there are, obviously, some really impressive benefits from using monads as well (i.e. try not to think of monads as something being shoved down your throat by mathematical pedants). If you continue to use Haskell, you will likely find some such uses (on the other hand, you are every bit correct about the performance of many Haskell programs, but programmer time costs more than CPU time). One that I found pretty impressive is the ability to implement a nondeterministic parser using monads to represent parse matches or non-matches--a case where monads don't just wrap I/O or other state, but acctually do something cool...

    24. Re:My realworld results differ by Anonymous Coward · · Score: 1, Informative

      Ocaml doesn't support any ad-hoc polymorphism (overloading) whatsoever in functions.

      This is an annoyance more than anything else... e.g. writing "+." for floating point addition instead of "+"...

      Methods on the other hand can be overloaded, but not generic.

      Actually, the language _has_ been extended recently to allow a restricted form of generic methods. For example, you can now do fold's using a syntax like this:

      class ['a] list l =
      object
      val mutable _data = l
      method fold : 'b . ('b -> 'a -> 'b) -> 'b -> 'b = fun f init = List.fold_left f init _data
      end

      Having to differentiate between HashTable.insert and SkipList.insert sort of defeats the purpose of abstract types, because no one thought to make module signatures themselves first-class

      Modules are not first-class in the language proper, but they are first-class in the functor language. So you can abstract over Hashtable.insert and SkipList.insert by using:

      module type DICTIONARY =
      sig
      type t
      type key
      type value
      val create : unit -> t
      val insert : t -> key -> value
      end

      module Foo (D : DICTIONARY) =
      struct
      let foo () =
      let dict = D.create () in
      D.insert dict ....
      end

      This isn't as powerful as what Alice does (the core language and functor language are more closely integrated in Alice), but, of course, you could get similar effects by using objects in OCaml...

      Ocaml isn't seeing a lot of new work going into it -- the language definition seems to have become cast in stone.

      Not quite: generic methods are relatively new (added, I think, last year). The functor language has also been extended a bit in the last little while...

    25. Re:My realworld results differ by snorklewacker · · Score: 1

      > As you pointed out, Haskell doesn't support operator overloading, either,

      Erm, it most certainly does support operator overloading. Operators, like in any language of the ML family, are merely functions written infix.

      GCaml looks like a nice start, but it looks about as active as O'Haskell. I suppose I can stick with modules and functors or pure OO to get genericity in ocaml... I just get the feeling that I'll never be able to effectively glue these two separate systems together, let alone use them piecemeal.

      I also agree that there's awesome power in monads, and they elegantly express complex transformations (sort of ... have you ever had to count on your fingers exactly the right the number of liftM calls?) but there's just such a dearth of good example material on all the Control.* monads that do so much of the heavy lifting... I'm in no position to write such material, either, so all I can do really is kvetch about their lack.

      --
      I am no longer wasting my time with slashdot
    26. Re:My realworld results differ by snorklewacker · · Score: 1

      Yowza, mod parent up into the stratosphere. I stand at least partly corrected, not being aware of generic methods. I know about functors, but the disconnect between them and the actual language just feels so bloody bureaucratic compared to type classes -- which are also in a separate language, but it's a small one in comparison.

      I guess I should take another look at Ocaml. My other long-running gripe about ocaml was its lack of machine words (like UInt64), and while I'm not fond of having only a library solution without support for overloaded operators (yes I like my overloading), I'm happy to read that the compiler is actually aware of them and keeps them unboxed wherever possible.

      Now I just need a project worth doing ... seems everything I do these days is one-offs in perl. :p

      --
      I am no longer wasting my time with slashdot
    27. Re:My realworld results differ by angst_ridden_hipster · · Score: 1
      Oh, and it's "moot point" (no idea where the word moot comes from) and "hear, hear" (as in "we hear you". Or "preach it!").

      Actually, that was my point, even if I made it poorly.

      And a "moot" is a gathering of freemen in Merrie Olde England. How the meaning of "moot point" evolved from a point of discussion to a point not worthy of discussion is an interesting quirk of language. My understanding, however questionable and shaky, is that the arc went from "point of debate" to "debatable" to "already debated" to "of no significance / unworthy of debate."

      I'm sure an etymologist will step in and give us the lowdown on this.

      --
      Eloi, Eloi, lema sabachtani?
      www.fogbound.net
    28. Re:My realworld results differ by Anonymous Coward · · Score: 0

      Yowza, mod parent up into the stratosphere.

      Thanks :) Wish someone had paid attention...

      My other long-running gripe about ocaml was its lack of machine words (like UInt64), and while I'm not fond of having only a library solution without support for overloaded operators (yes I like my overloading), I'm happy to read that the compiler is actually aware of them and keeps them unboxed wherever possible.

      Unfortunately, I think machine words are usually only unboxed when used in Bigarrays. This actually makes garbage collection more efficient, but if you do a lot of I/O with non-OCaml programs, then this is not nice for you (I don't have this restriction, so I don't mind).

      Yes, and as another poster mentioned there is GCaml, but you may have to wait some time for that to be integrated in the main language...

    29. Re:My realworld results differ by Bloater · · Score: 1

      That will work out very slow in at least ghc and hugs (not sure about nhc) as they seem to build the whole string in core before putStr can do its business. Use (mapM_ putStrLn lines) for the last line of code instead to get iterative behaviour instead of recursive.

    30. Re:My realworld results differ by jovlinger · · Score: 1

      You might have to solve the problem differently

      And therein lies the rub.

      I think almost everyone will agree that carefully written C (or ++) is faster than pretty much anything out there, including OCaml.

      I also (subjective statement warning) think most will agree that pretty much anything out there is easier to read / reason about / maintain than that C. Speed optimized C is rarely (tho not never) pretty.

      OCaml's strength is that is is almost as fast as C, almost as pretty(*) as haskell, and scales to large projects fairly well.

      I guess its true strength (IMHO) is that you can write 'dirty' ocaml for the criticial 10%, wrap that up in a nice interface, and access it from more idiomatic code, all without needing foreign functions and IDLs, and maintaining strong typing accross the application.

      (*) I do agree about the surface syntax being ugly, but that's not what I mean.

    31. Re:My realworld results differ by Tom7 · · Score: 1

      Having to differentiate between HashTable.insert and SkipList.insert sort of defeats the purpose of abstract types

      I disagree. The purpose of abstract types is to syntactically enforce layers of abstraction. Overloading can save you typing (in my opinion, it usually makes programs less clear in the process), but it doesn't really have anything to do with abstract types.

      Except for some questions of how they'd interact with the module system, Haskell's type classes are compatible with eager functional/imperative languages like ML, so I don't think your holy grail is that far off (except that the ML community doesn't like overloading much).

    32. Re:My realworld results differ by Anonymous Coward · · Score: 0

      Sorry. I misspoke. What I meant to say, I think, was that Haskell doesn't support function overloading in the way one commonly describes it (e.g. the C++/Java way).

      Typeclasses are definitely better, anyway, so it's kind of moot (you can use polymorphic types for defining functions that have something of an appearance of overloading, but this isn't at all the same thing, of course).

      I still think modules and functors are quite limiting, especially when compared to typeclasses, but they do get the job done. So I think this is, in fact, a very valid complaint with OCaml, but on the other hand, similar complaints (e.g. the less refined module system) are valid against Haskell.

      And yeah, I have had to count on my fingers the right number of lift calls, but I don't think that's *that* big a deal. Anyway, if it typechecks, it works, so no real harm if you're off the first time you try it. ;)

    33. Re:My realworld results differ by jovlinger · · Score: 1


      Haskell's type classes are compatible with eager functional/imperative languages like ML


      I seem to recall that someone tried a haskell compiler with a carefully broken strictness analyzer: it claimed that every function was strict.

      Unfortunately, it didn't work, I think due to required laziness in the prelude. I don't recall it being impossible.

      Would be a fun language to play with.

    34. Re:My realworld results differ by srussell · · Score: 2, Interesting
      They are indeed a part of the language, and definitely a new concept, but monads aren't nearly as confusing as people seem to think, certainly not more confusing than objects
      It isn't that monads are confusing, but that they're a contagion. Now, I'm not a very experienced Haskell programmer; in fact, I'm not a very experienced functional programmer, so I'm probably just making some dumb mistakes... but almost every application that I write goes through the following evolution:
      1. Application starts out at a high level, with function declarations, as I work the problem out. This is extremely elegant and natural, and defines the problem on paper nicely.
      2. Application gains some data structures and function definitions, as I start "filling in the blanks."
      3. At some point, I discover that I can't get around some problem without using monads.
      4. Trigger frantic rewrite of almost all of the code as one seemingly trivial function using monads suddenly requires all the functions in the call stack to use monads.
      Here's a really simple example that is so annoying, I'm convinced there must be a way to do it that I don't know about:

      -- Assume 'qsort' such that:
      qsort :: (a -> a -> Int) -> [a] -> [a]
      -- So that:
      qsort (\x y -> if x<y then -1 else if x>y then 1 else 0) [3,2,1]
      -- Then an array randomizer could be:
      qsort (getRand) somearray
      But not if you want to use randomIO, which provides non-seeded (or, rather, IO seeded) numbers. Once you get that IO monad in there, it makes it impossible to use any standard non-IO aware higher order functions.

      Higher order functions are one of the cool Haskell features, and monads severely restrict their use.

      --- SER

  5. Apples and oranges by Anonymous Coward · · Score: 0

    OCaml is a multiparadigm language. C++ is also a multiparadigm programming language - there are libraries that let you write functional programs in C++. So when comparing these two languages there is no excuse for comparing completely different algorithms. The whole point of a multiparadigm language is that it lets you choose the paradigm that fits the problem!

  6. Other languages... by jd · · Score: 0, Offtopic
    D is supposedly superior in almost every regard to C++. There is a D interface to GCC produced by a third-party. It's not tested in the shootout, but the reference implementation of D (which is stand-alone) is.


    Occam is probably the most scalable language out there, as it was designed for massively parallel systems.


    There's a PL/I interface to GCC. I dare someone to write a Gnome or KDE interface for it...

    --
    It's a small world and it smells funny; I'd buy another if it wasn't for the money; Take back what I paid (SoM)
    1. Re:Other languages... by Anonymous Coward · · Score: 1, Interesting

      D has no support though. At least when compared to other modern languages (C/C++, C#, Java, Perl, Python, etc.). It really not all that different from C++. It has some better syntax and extra features, but it's simply non-standard and the tiny improvements aren't worth the risk of using something unproven. It is a dead end.

    2. Re:Other languages... by jd · · Score: 1
      True, maybe, but so was PHP when it started and Python 1.x had only a single developer for the core system. Java is largely under the control of Sun, with very very few clean-room implementations. Kaffe is one of the few I know of, and it is far from feature-complete.


      On the flip-side, there are plenty of languages which were "official" standards, once, but which are now all but dead. ADA was the "standard" language of the DoD, for example, was (and is) very well supported, but is simply incapable of competing with the other languages out there.


      I guess what I'm saying is that there are plenty of languages which were once "unproven", "non-standard" and "unsupported", but which have proven themselves over time. Most languages start that way. There are also plenty of languages which are "proven", "standard", "supported" and total failures in the real world, for whatever reason.


      This is not to say D is "good", or should be used. Rather, it is to say that the success and failure of computer languages is unpredictable. The same is true of any computer technology or IT-based market. The only way to survive, in this industry, is to have the skills people want both now and next week. D may be the branch that C follows, the way C is the branch of B that survived, which in turn was the branch of BCPL that survived. I'd rather learn a skill I don't need than lack a skill that's essential. Once you hit a crisis point (dot-com bursts, for example), it is too late to learn something new.

      --
      It's a small world and it smells funny; I'd buy another if it wasn't for the money; Take back what I paid (SoM)
    3. Re:Other languages... by Anonymous Coward · · Score: 0
      D is supposedly superior in almost every regard to C++.


      Super. We know that the supposedly superior language always wins too, that is why we are all programming in Common Lisp. Really, what makes D so superior?
    4. Re:Other languages... by bigsteve@dstc · · Score: 1
      Java is largely under the control of Sun, with very very few clean-room implementations. Kaffe is one of the few I know of, and it is far from feature-complete.

      There are actually quite a few clean-room Java implementations. The GNU Classpath homepage lists 14 Java VMs that use the Classpath clean-room implementation of the Java class libraries.

      For the record, Classpath is 99.79% function complete relative to JDK 1.1 according to the comparison linked from the home page. The numbers drop off with JDK 1.2. Some bits of Swing are missing and most of the org.omg.corba hierarchy is excluded for reasons to do with the OMG's copyright notice.

    5. Re:Other languages... by Anonymous Coward · · Score: 0

      No, you're comparing languages which have no counterpart.

      D is extremely close to C++, C#, and Java. So close that its tiny improvements are pointless. It's not a whole new language that might benefit all or have some revolutionary effect, it's just a twist on other more popular languages. No major coporations are supporting it, it's basically just one guy. It's a dead end.

    6. Re:Other languages... by Anonymous Coward · · Score: 0

      Occam is probably the most scalable language out there, as it was designed for massively parallel systems.

      Occam != OCaml!

    7. Re:Other languages... by Anonymous Coward · · Score: 0

      Two things, I am guessing that you really do not know Lisp. Good 'ol s-expression Lisp lots rather goofy and intimidating to people who do not know it but after some use it seems perfectly normal. The second thing, Lisp does not have to have s-expression syntax. Dylan is perhaps the best example of a Lisp with m-expression syntax and I believe that Paul Graham's Arc will too allow m-expressions.

    8. Re:Other languages... by Anonymous Coward · · Score: 0

      I know Lisp (I've been programming in Lisp for almost 20 years). Everything I said is based on that. I stand by what I said. Lisp still sucks.

    9. Re:Other languages... by Anonymous Coward · · Score: 0

      Talking with a Lisp does not count.

    10. Re:Other languages... by Anonymous Coward · · Score: 0
      ADA was the "standard" language of the DoD

      The language you are refering to is Ada, named after Ada Lovelace, not ADA.

    11. Re:Other languages... by Anonymous Coward · · Score: 0

      then why have you been using it for the past twenty years?

      Because I'm extremely good at what I do (programming). Sometimes clients already have systems in place that were done in Lisp. I need to be able to fix or modify their stuff.

      The point that the other poster made about m-expressions is also very valid- there is nothing stopping you, or anyone else, from creating a new syntax for Lixp.

      You can not completely change the syntax. There will always be Lisp-like idioms, no matter what you do. If there are not, then it ain't Lisp.

      As for your speed concerns, in my experience Lisp is faster than Java (but what isn't).

      Java sucks too (yes, I've been using Java since it was just a beta language).

      And good Lisp systems have been shown to generate code better C compilers.

      No. All examples I have seen of this are contrived. Either they are comparing against crappy C compilers or crappy C code (ie. they compare C code written like you would write a Lisp program, which doesn't work in C's favor).

      C is essentially bare on the hardware. It pretty much maps directly to machine code. You can't get any faster than that without going to assembly language. Any inefficiencies are do to poor programming.

    12. Re:Other languages... by Anonymous Coward · · Score: 0
      Well it is true you are an idiot spewing the ever popular C is essentially on bare metal non-sense.

      Anyhow, I suppose something like:
      define function describe-list(my-list :: <list>, #key verbose?) => ()
      format(*standard-output*, "{a <list>, size: %d", my-list.size);
      if (verbose?)
      format(*standard-output*, ", elements:");
      for (item in my-list)
      format(*standard-output*, " %=", item);
      end for;
      end if;
      format(*standard-output*, "}");
      end function;
      is not Lisp, right?
    13. Re:Other languages... by Anonymous Coward · · Score: 0

      Technically, no, it's not. It's Dylan. The whole list syntax has been destroyed.

      Many people do not think Dylan is really LISP.

      It's a derivative for sure, but LISP it is not.

    14. Re:Other languages... by Anonymous Coward · · Score: 0

      And your point is? You refute nothing that I said. This means I'm at least correct and you have no basis for saying I am not a master.

    15. Re:Other languages... by Anonymous Coward · · Score: 0

      Its Lisp your loser. Just with m-expression syntax.

    16. Re:Other languages... by Anonymous Coward · · Score: 0

      Do personal insults help you get over your technical ineptness?

    17. Re:Other languages... by Anonymous Coward · · Score: 0

      It seems like the guy does not even know what a m-expression is.

    18. Re:Other languages... by Anonymous Coward · · Score: 0

      Don't feel bad, what I consider master is maybe 1 programmer in 100 or maybe one 1000- yeah probably 1000. Anyhow, you have no basis to suggest that you are anything close to master without putting up some serious code.

      The point about saying that you know many languages but are a master of none is that you only know programming in broadstrokes. Your comments about C definitely help to support that. Let me throw two quotes at you "It is easy to write Lisp. It is easy to write slow Lisp." and "It is hard to write slow C programs.". It definitely sounds like both quotes apply to you. Just because the Lisp code you write is slow does not mean anything about other people's code.

  7. Can you also link to the Haskell code? by shapr · · Score: 2, Interesting

    I'd like to see the Haskell sources for comparison.
    http://minorgems.sf.net/Haskell.hs doesn't exist, though the the C++ and OCaml code are there.

    --

    Shae Erisson - ScannedInAvian.com
  8. apples and oranges by e+aubin · · Score: 3, Insightful

    Usings lists in all circumstances because its functional is not appropriate. Show the ocaml implementation using arrays or an c++ implementation using linked lists for a valid comparision.

    The strength of Ocaml is the flexibility it provides to a developer. If your solution is more elegantly coded using imperative constructs, then use them!
  9. Speed alone by idiotfromia · · Score: 0, Flamebait

    If you're just looking for speed, try assembly. I hear all the good programmers are using it these days.

    1. Re:Speed alone by ameline · · Score: 2, Interesting

      I know you think you're joking, but you're actually right -- that's what the good programmers do. In my application, the performance critical routines (on the order of a dozen or so) are hand coded in vector assembler (Altivec on Mac, and MMX and SSE on Intel), and are about 3 to 4 times faster than the most optimized C or C++ implementation of those algorithms. If you have code that is vectorizable, and especially if it is doing saturated small integer math, (blending, resampling etc), you can do way better than any existing compilers by hand coding in assembler.

      --
      Ian Ameline
    2. Re:Speed alone by ratboy666 · · Score: 1

      Ameline:

      Um... not true.

      I consider myself an expert assembly level programmer. I recently took on a job optimizing some C code. The code was the back end bi-level compression code in a JBIG library. Bit twiddling, and updates to a compression table.

      No big deal, I though. Unrolled the inner loop, and extracted patterns (as in "this case cannot possibly happen on the next n bits being 0, so bulk update and skip forward"). I wrote a program to generate the cases of interest, and tested.

      Very good results -- client is happy. I converted to assembler, hand scheduling instructions. On a Pentium II (400Mhz) that I used for my speed test machine, I handily beat GCC (3.something). By a factor of 2, which was expected. Compiling the code using Microsoft C from the .NET distribution... they beat me. By a healthy margin. Almost 20% in the inner loop!

      Analyzing the result and checking Intel documentation... It shouldn't have been that way. However, Microsoft may have better connections with Intel, and therefore better documentation on the processor.

      On code that is vector code -- yes, I will agree with your point. But I think that advantage will fall by the wayside as compiler vendors implement C99 features, and auto-vectorization.

      But don't sell the current crop of C compilers short - in particular, MSVC 7 is VERY VERY good at generating x86 code.

      Ratboy (hanging up the assembler badge, sadly).

      --
      Just another "Cubible(sic) Joe" 2 17 3061
    3. Re:Speed alone by jovlinger · · Score: 1

      how about intel's compiler?

    4. Re:Speed alone by ratboy666 · · Score: 1

      Since the job was T&M, I didn't have the freedom to explore Intels compiler. Sorry -- and it would have been very interesting.

      Ratboy.

      --
      Just another "Cubible(sic) Joe" 2 17 3061
    5. Re:Speed alone by Anonymous Coward · · Score: 0

      and are about 3 to 4 times faster than the most optimized C or C++ implementation of those algorithms.

      So instead of taking 20ms it only takes 5ms? Cool!

    6. Re:Speed alone by ameline · · Score: 1

      I did explicitly say "if it's vectorizable", and "particularly if it uses small integer saturated math". I doubt any autovectorization will recognize the conditionals necessary for saturated math and make efficient use of MMX instead. (I worked on the back end of an optimizing compiler at IBM for 6 years, so I know a bit about how compilers work :-)

      --
      Ian Ameline
    7. Re:Speed alone by ameline · · Score: 1

      I've tried 'em all. Intels is good, but when it comes to toe sort of code I was talking about in my original post, you can still get a factor of 2 or 3 over whan it can do with scalar code, if you havd code a vectorized MMX or SSE version.

      --
      Ian Ameline
  10. lookup table by jefu · · Score: 2, Interesting
    Looking at the ocaml code and in particular at the functions that handle the memoizing, I'm wondering how big those memo tables are getting. If they get big it seems quite possible that the overhead of doing the lookup (or if the lookup is fast) of doing the insert might not be a problem. It should be easy enough to just look and see how big the table is getting.

    In the same kind of vein, has the code been profiled? I'd quite like to see where the time is going.

    1. Re:lookup table by PylonHead · · Score: 5, Insightful

      Yea, you're not kidding.

      I just looked at the code, and he's memoizing the function results in a associative list:

      (* Set up an associative list for memoization *)
      let lookup key table = List.assoc key !table;;
      let insert key value table = table := (key, value) :: !table;;

      Insertion is cheap, but the lookup is a linear table scan! Doh! What was he thinking?

      I suspect that a Hashtable or a Map datastructure might be much better suited to the task.

      In any case, it would have been very easy for him to post this code to the OCaml newsgroup and ask, "Am I writing good functional code?"

      He would of gotten a lot of advice on how he could have sped up his program while still maintaining a functional style.

      Lastly, in response to his question, "I could write an OCaml implementation that is isomorphic to the C++ code (using loops and side effects), but what would be the point?" The point is that you can easily mix and match styles in OCaml.
      You can write 90% of your code in a functional style and fall back to imperative style if there is an inner loop that would benefit from that.

      For this problem though, I suspect that a well written functional version would be pretty close in speed to his C++ version, cleaner, and easier to maintain.

      --
      # (/.);;
      - : float -> float -> float =
    2. Re:lookup table by Anonymous Coward · · Score: 0

      cleaner? heh... in some functional languages maybe. not ocaml though, blech

    3. Re:lookup table by Anonymous Coward · · Score: 0

      Yea, you're not kidding.

      What are you cheering about? Or perhaps you meant, yeah?

    4. Re:lookup table by Anonymous Coward · · Score: 0

      Out of curiosity i replaced the table stuff with hashtbl.

      Original code: (time output on an amd64 3000, bytecode)

      real 0m2.560s
      user 0m2.532s
      sys 0m0.001s

      Hashtbl code: (same system, bytecode)

      real 0m0.312s
      user 0m0.303s
      sys 0m0.004s

      So just for using Hashtbl, we got an 8-times increase in speed. This is obviously a problem with using an O(n) lookup on tables with possibly millions of entries.

    5. Re:lookup table by vanicat · · Score: 1

      Looking at the ocaml code and in particular at the functions that handle the memoizing, I'm wondering how big those memo tables are getting.

      Well, this is not the problem : at the end of the execution, his table are empty. The problem is that the memoization is not done. Well after using Hashtable in place of assoc list, and after having realy memoize all function (only one had the problem) I've a good speedup...
    6. Re:lookup table by Anonymous Coward · · Score: 0

      I really don't understand this "OCaml is ugly" meme that the trolls seem to like so much. Compared to C++ with its hideous verbose template syntax, or Lisp with its unreadable parentheses, or "const static final verbose int Java", OCaml is positively pleasant to work with. The one single language I find more beautiful is Haskell, and Haskell is also dead slow and incomprehensible to anyone without three PhDs in advanced mathematics.

    7. Re:lookup table by Anonymous Coward · · Score: 0

      Yeah;; OCaml;; Has;; A;; Great;; Syntax;; ;; No;; verbose;; extra;; Anything;; required;; at;; all;; ;; ;;

      Of course that's just one part of OCaml, there are much larger problems with its syntax. For anyone interested, compare SML or Erlang to OCaml some time.

    8. Re:lookup table by Fahrenheit+450 · · Score: 1
      Yeah;; OCaml;; Has;; A;; Great;; Syntax;; ;; No;; verbose;; extra;; Anything;; required;; at;; all;; ;; ;;

      Um... you do realize that you don't need the ;; unless you're working in the toplevel, right? Here's a snippet from the List module's source:
      let rec length_aux len = function
      [] -> len
      | a::l -> length_aux (len + 1) l

      let length l = length_aux 0 l

      let hd = function
      [] -> failwith "hd"
      | a::l -> a

      let tl = function
      [] -> failwith "tl"
      | a::l -> l

      let rec nth l n =
      match l with
      [] -> failwith "nth"
      | a::l ->
      if n = 0 then a else
      if n > 0 then nth l (n-1) else
      invalid_arg "List.nth"

      let append = (@)

      let rec rev_append l1 l2 =
      match l1 with
      [] -> l2
      | a :: l -> rev_append l (a :: l2)

      let rev l = rev_append l []
      Yep... chock full of ;;...

      And as already mentioned, there's the revised syntax if you prefer it, or hell... feel free to go plum loco and use camlp4 to write your own syntax.
      --
      -30-
    9. Re:lookup table by jefu · · Score: 1

      Yup, thats what it looked like to me, but I'm not enough of an OCaml expert to know for sure if that was what he was doing, and I wasn't sure about how to profile it and find out how big the lists were getting. If the lists are always short, there should be no problem, but if they get long, things could get very slow.

    10. Re:lookup table by Anonymous Coward · · Score: 0

      No, he means, "Yea, and verily!"

  11. OCaml is supposed to be faster? C++ is dynamic? by Anonymous Coward · · Score: 0

    Did I just enter bizarro world? A "functional" language like OCaml is usually slower. And C++ is not for dynamic programming...?

    Hmm, as other posters have pointed out, such a gigantic difference in speed is an implementation or algorithm choice issue, not a difference in language speed.

    I bet if you optimize your program properly, it will be on the same order of magnitude in speed.

    Note: I'm not suggesting that OCaml (I actually am more familar with Ruby, Lisp, and Haskell) is always slower than C/C++. In fact those higher-level languages often allow you to express your code in such a way that faster solutions become obvious. I.e. they allow you to gain better insight into your program.

    1. Re:OCaml is supposed to be faster? C++ is dynamic? by pclminion · · Score: 1
      And C++ is not for dynamic programming...?

      You apparently don't know what "dynamic programming" means. Dynamic programming is a general technique, usually applied to search, which caches the results of executions on problem subsets and then reuses those results when those subsets are encountered again. It has nothing to do with dynamic typing, or dynamic code, or whatever you're thinking about.

      To put it extremely simply, dynamic programming means hash tables, although that's not really a complete definition.

    2. Re:OCaml is supposed to be faster? C++ is dynamic? by Anonymous Coward · · Score: 0
      Try this for a reality check as to what "dynamic programming" means, if you can grok it.

      IMO caching and hash tables are optimization details for implementation, not the heart of what there is to undestand.

    3. Re:OCaml is supposed to be faster? C++ is dynamic? by xenocide2 · · Score: 2, Informative

      Ironically, the OCaml version posted uses a list rather than a hash table. I'm busy making dinner right now, but I'm certainly interested in seeing if that can be improved upon.

      Perhaps this is a case of problem solving by public contradiction?

      --
      I Browse at +4 Flamebait

      Open Source Sysadmin

    4. Re:OCaml is supposed to be faster? C++ is dynamic? by crmartin · · Score: 1

      "Bellman". Google "Bellman" "dynamic programming".

      Just to get some historical context.

      Interestingly, "dynamic programming" wasn't really even meant as a computer-programming technique.

    5. Re:OCaml is supposed to be faster? C++ is dynamic? by Anonymous Coward · · Score: 0

      Actually, it's pretty far from a hash table. It's much closer to filling out a two (or more) dimensional linked list where the links are strictly to nodes that are equidistant or further from the starting node. Almost any algorithms book (Cormen for example) has a good treatment of dynamic programming - algorithms like matrix chain multiplication, longest common subsequence, etc...

      If you used a hash table, you'd have to have links across the lists for each hash value. I think "not really a complete definition" should be "not really a correct definition.".

    6. Re:OCaml is supposed to be faster? C++ is dynamic? by pclminion · · Score: 1

      Yeah... I got dynamic programming and memoization confused.

  12. About benchmarks by dtfinch · · Score: 1

    I've seen different C compilers with all speed optimizations enabled generate code for the same problem that differed in performance by a factor of 20. This was not consistent with other benchmarks I had seen on the web comparing the same compilers. The benchmark was an html compressor. I'd share more, but unfortunately the code was automatically deleted by XP's chkdsk after a minor crash.

    One algorithm is sometimes not enough to accurately judge between compilers. But I guess if it's OCaml, then sure, C++ is faster.

  13. C++ or C? by Anonymous Coward · · Score: 0

    Your C++ code looks more like C code to me.

    1. Re:C++ or C? by Anonymous Coward · · Score: 0

      But is has c++ style comments. (though iirc that was back added to the C spec).

      But hey structs can be used in good OOP design, they just never are.

    2. Re:C++ or C? by Anonymous Coward · · Score: 0

      C++ style comments are officially part of the C standard (C99 that is).

      Although you can use structs to do OOP, they are like using pure virtual C++ objects for everything. That is, you will suffer performance penalties because all the "methods" are function pointers that have to be dereferenced. C++ is faster when doing OOP. Plus C++ gives you OOP type safey that you just can't get in C.

  14. Haskell by jefu · · Score: 1
    I quite like haskell in general and finally figured out enough about monads so I can actually use them.

    But now it all seems to be about Arrows. AAARRRGGGHHH!

    Too bad. Haskell really is one of the coolest languages around.

    1. Re:Haskell by Bloater · · Score: 1

      Check out Epigram, hopefully they'll think of a better "helper" program, and a syntax that doesn't kill you to write without the helper (and that is clearer even with the helper).

      And one day it may even be capable of I/O.

  15. this is not possible by Anonymous Coward · · Score: 0

    I've done a LITTLE experimenting with OCaml in the past and liked what I saw but I don't really OCaml well so I won't try to comment on the actual specifics of your code (I do know C++ well), BUT...

    I can absolutely guarantee you that if your OCaml implementation of this algorithm is over 960 times slower than your C++ implementation, something is wrong with your OCaml implementation. There is no chance that the same algorithm is 960 times slower in OCaml than C++. Why not ask for help on the OCaml mailing list (ie, the people who know about this stuff) rather than on Slashdot (ie, the people who don't)? They have been really helpful to me in the past.

  16. Haskell code? by Pseudonym · · Score: 4, Informative

    Can we see your Haskell code?

    Haskell is not known for raw speed, but dynamic programming is probably the one thing it does well, thanks to lazy evaluation. You fill a CAF with unevaluated function calls, and the language engine does the rest. It won't be as fast as the hand-crafted C++ version, most likely, but if your O'Caml code is anything to go by, it might be able to be improved.

    --
    sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
  17. Your problem(s) by Anonymous Coward · · Score: 0, Informative

    Your problem is that you don't understand how OCaml was meant to be used. We do not have a variety of data structures and imperative features so you can go use lists when arrays are appropriate. This is not Haskell. Furthermore, you redefine numerous standard library functions... and not only that, your versions aren't tail recursive? Why would you do such a silly thing? No wonder your code is so long.

    Basically what you need to do is sit down, stop being such a dumbass, read the manual, and don't pretend your false conceptions of how OCaml is "meant to be used" (your code is stylistically and algorithmically awful) are in any way relevant to its actual performance. If you wrote similarly stupid code in C++, it would be just as slow.

    You can actually get a constant 8x speedup if you fix your memoization, but the real problem lies in the algorithm itself. Unfortunately, the code is unsalvageably bad.

  18. It's always possible to tune your inner loops by Tom7 · · Score: 4, Informative

    One of the joys of programming in ML is that you can write most of your code in a really nice, functional way, and (if necessary) put in the effort to write a tight inner loop, perhaps in an imperative style for speed. I don't see this as a disadvantage, and ML compilers often do a better job of optimizing such loops than C compilers, in part because of more information being available in the type system. (And if they don't, it's trivial to invoke C subroutines.) Also, if performance is really an issue, you might try mlton (which is for SML, very similar to Caml); its whole-program approach often produces significantly better code than O'Caml.

    However, as an every-day ML user I find it very unlikely that your program would be a thousand times slower if you're using it "the way it's meant to be used." I am guessing that your implementation is asymptotically worse, since using map and fold correctly should really only be a constant factor slower than C, at worst. (mlton can often inline and optimize these into essentially the same code you'd write in C!) How about posting your code?

    1. Re:It's always possible to tune your inner loops by Tom7 · · Score: 1

      My mistake, the code is posted.

    2. Re:It's always possible to tune your inner loops by CaptainPinko · · Score: 3, Insightful

      I for one would be interested by how much you could --as a regular ML user-- optimise the code and see what kind of performance you could get. Really there are no slow languages, only slow implementations.

      --
      Your CPU is not doing anything else, at least do something.
  19. FFTW. by Anonymous Coward · · Score: 0

    FFTW is written in O'CaML. What you compile against is the C library O'Caml generates.

    Often it's how you use the tool... not the tool itself. I've got C++ code that runs circles around C code.

    This will happen don't be surprised.

  20. Bad string manipulation by vanicat · · Score: 4, Informative
    I've not read all the ocaml code, but I've seen at the very begining this:
    let rec printCells cs =
    match cs with
    | [] -> ""
    | (c::rest) -> (printCell c) ^ (printCells rest);;
    And I know that this program will be slow. The ocaml string concatenation operator copy both string each time it is called, and this concatenation work will take O(n^2) step.

    You should use the Buffer module, or String.concat:
    let rec printCells cs =
    String.concat "" (list.map printCell cs)
    If there is a lot of those mistake, no wonder it is so slow...
  21. it runs in about 10 seconds by sdnin · · Score: 1

    Well, I tried the code and on my computer it ran in about 10 seconds (ocamlc/bytecode) and in about 7.6 seconds (ocamlopt/machine code). So, what's the problem this discussion is about?

    1. Re:it runs in about 10 seconds by sdnin · · Score: 1

      Well, ok, it's not 7x3 in the original code.

      But after looking into the code in more detail,
      I saw that many (the most (nearly all (all?!)))
      recursive functions aren't tail-recursive.

      With such simple recursive approaches
      it is no wonder that the program is slow.

      Try to read SICP and learn to write tail-recursive
      code, then the code will be reasonably fast!

      At least the functions that will be called often
      should be rewritten into tailrec functions.

      ocamlprof will help to find out, which functions
      are called most often.

    2. Re:it runs in about 10 seconds by sdnin · · Score: 1

      Section 1.2.1 in SICP: http://mitpress.mit.edu/sicp/full-text/book/book-Z -H-11.html#%_sec_1.2.1 ... programming is an art... ...maybe one that C++ does not force you to learn... :->

  22. More efficient ocaml version by Anonymous Coward · · Score: 3, Interesting

    I see nothing wrong in your C++ version, while your ocaml version clearly sucks: you are memoizing using a complex key, and an association list, meaning that accessing memoized information costs a lot.

    If you are concerned by performance, you should use a complete cache, like in your C version.
    FYI, I uploaded an ocaml translation of your C code. It doesn't use mutable state except for memoizing, and uses pattern-matching on lists, and recursion rather than for loops, but otherwise it follows closely your code. Performance should be very similar.

    http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/garde n2.ml

    1. Re:More efficient ocaml version by jdh30 · · Score: 2, Interesting

      Interesting, having tidied up the C++ code, this OCaml code is still less than half the length. The original OCaml clearly had several unused functions, several pointless reimplementations of the library functions and many comments regarding Haskell (?!).

      In terms of performance, I get:

      $ ./garden2
      real 0m16.951s
      user 0m16.870s
      sys 0m0.010s

      $ ./Garden
      real 0m10.200s
      user 0m10.160s
      sys 0m0.010s

      So OCaml wins on performance per LOC. :-)

      However, this C++ is also very poorly written IMHO. Specifically, it should use the STL and, particularly, vector<bool> to implement bitmaps. That would be a better contender...

    2. Re:More efficient ocaml version by Anonymous Coward · · Score: 0

      However, this C++ is also very poorly written IMHO.

      I agree. It's not clear why C+ was chosen, because almost all of it is compilable as C. Heck, he didn't even bother to omit the superfluous occurrences of "struct". I guess he wanted to use "new".

      Oh, and it will leak memory if there is an exception thrown while allocating memory.

  23. Man, this is not C++! by gothmog666 · · Score: 1

    You wrote it in pure C! You are comparing Ocaml with C, not C++! You are just using the C in C++.
    Try writing your program fully using OO and templates. I dare you.

    Another thing: You OCaml code is shity. Im not asking you to tinker with optimisations, but just to use the correct data structures.

    It's not fair compare lazy OCaml code to simpleminded C code.

    --
    I intend to live forever. So far, so good.
  24. Email response by jcr13 · · Score: 5, Interesting

    > Here's a laundry list of why your O'Caml program in inefficient:
    >
    > 1. You use lists. Lists aren't designed to be fast (computationally)
    > to use. They're designed to be fast (programmatically) to use. You'll
    > be hard pressed to find a production, speed-sensitive Lisp or O'Caml
    > program that uses lists.

    Okay... but here's my point: Every single example that shows how elegant Haskell and OCaml are uses lists. The 4-line Quicksort example for Haskell uses lists. All of the code that demonstrates easy reuse of functions and functions taken as arguments uses lists (like how easy it is to implement quite complicated algorithms using only map and filter, for example).

    So, proponents say "Everyone should use functional languages because they can express complicated problems in elegant ways and result in cleaner, more reusable code."

    But what you're saying in #1 above is that in "production," speed-sensitive code, no one is using lists... this would mean that no one is using map, filter, or any other pieces of reusable primitive code. So, they are instead all using mutable data structures... I.e., they are programming with side-effects and loops (random access instead of recursion, even when ever element of an array/list needs to be accessed/processed).

    That was my point exactly. If you write elegant OCaml code using all of the lovely (and I mean lovely, really) tricks that they present when they demonstrate why OCaml is cool, you end up with code that is too slow to use in the real world.

    I would say that my C++ (or most would call it C) implementation is elegant enough... easy to understand... no messy optimization tricks. Sure, I'm not using objects and templates everywhere, but these structures are hardly needed to solve this simple problem.

    > 2. Practically none of your functions are written tail-recursively.

    Good point.

    > 2.5. You use a list append (@) inside a loop (generateStates).
    > List.append is O(m), where m is the length of its first argument. If
    > you write an implementation, you'll see why. It probably doesn't make
    > much of a difference here (generateStates is only called once) but it's
    > something to watch out for.

    Of course, as you point out, generateStates has almost no effect on the running time. However, I wonder how you might implement that in an elegant way in OCaml without @. In C, I just looped over all numbers between 0 and 2^stateLength and converted the bit representations for the numbers to cell on/off states.

    > 3. For Pete's sake, man, you're using an association list for your
    > memos! Surely you know that lookup in an association list is O(n) in
    > the size of the list.

    I simply Googled for "memoization Ocaml" and found that code:
    http://www.emeraldtiger.net/modules.php?op= modload &name=News&file=article&sid=9

    The author pointed out how "sweet" polymorphism is... one block of code that can be used to memoize any function. Sweet indeed, and it certainly sped up my OCaml code a lot (without memoization, it was so slow as to be intractable for anything larger than about 4x4).

    So... maybe you can re-write higher-order memoization code using more efficient data structures? I would love to see that code, and I'm sure the OCaml community would benefit from having that in their toolbox.

    I agree that the memoization code is probably the problem in the OCaml version. However, this code came directly from the OCaml community and was the *only* example of memoization in OCaml that I could find.

    For Haskell, I used an infinite list of results that was filled in lazily as the results were needed. This also sped up the algorithm dramatically. However, I cannot get a Haskell compiler to compile itself on my platform, so I was testing all code in the Hugs interpreter, which made it too slow to be practical. Isomorphic compiled OCaml code was hundreds of times fast

    1. Re:Email response by be-fan · · Score: 2, Insightful

      But what you're saying in #1 above is that in "production," speed-sensitive code, no one is using lists... this would mean that no one is using map, filter, or any other pieces of reusable primitive code.

      Only to the extent that all of your production code is speed-sensitive. The hot-spots are generally only 10% of the code. A lot of very complex code (eg: configuration stuff), only gets run very rarely. Using high-level languages has a certain design procedure. You write everything at the high-level, and verify that it works. Then, you look at the optimizer and see what to fix. If your compiler is good, you'll only have to write a fraction of the code at a low-level, and you'll have a net time benefit.

      --
      A deep unwavering belief is a sure sign you're missing something...
    2. Re:Email response by Fahrenheit+450 · · Score: 4, Informative

      Okay... but here's my point: Every single example that shows how elegant Haskell and OCaml are uses lists. The 4-line Quicksort example for Haskell uses lists. All of the code that demonstrates easy reuse of functions and functions taken as arguments uses lists (like how easy it is to implement quite complicated algorithms using only map and filter, for example).

      Or perhaps more correctly, "every single example that you've seen". For a real quick one, look at Jason Hickey's Intro to OCaml (pdf) and have a quick peek at his Red/Black tree implementation. Or even cooler (if you're into that sort of thing) is the ever famous One Day Compilers talk.

      But what you're saying in #1 above is that in "production," speed-sensitive code, no one is using lists... this would mean that no one is using map, filter, or any other pieces of reusable primitive code. So, they are instead all using mutable data structures... I.e., they are programming with side-effects and loops (random access instead of recursion, even when ever element of an array/list needs to be accessed/processed).

      No. What he's saying in that you should use the best data structure for the job. Your best bet would have been to use the Hashtbl module from the standard library, or if you wanted to stay in the purely applicative, the Map module (also in the standard library) would have been loads faster...

      You are aware that there are more purely functional data structures (pdf) (OCaml implementations) than the list, don't you?

      So... maybe you can re-write higher-order memoization code using more efficient data structures? I would love to see that code, and I'm sure the OCaml community would benefit from having that in their toolbox.

      Here's a pretty neat example that uses arrays in a naive way, but you could certainly use, say, a map instead... And I'm pretty sure the OCaml community (by which I mean the people who would have helped you improve your code had you asked them) know about things like this.

      I don't think I spread any falsehoods. I mean, my experiment was real, and the results are real, and the code is there for people to inspect and try on their own. I also talked in my /. post about OCaml code that is isomorphic to C being fast, but functional code perhaps not being fast.

      Yes. And we inspected it, found it to be poorly written, and told you so. The "falsehood" here is that you claim that code written in a functional style is slow, when you really should have said "my code written in a naively functional style is slow". If I fill my gas tank with water, my car sure is slower than walking, therefore all cars are slower than walking... right?

      Trust me... I am *dying* to use OCaml or Haskell for real-world programming. I have spent the past month or so exploring these languages and trying to apply them to real programming problems. Especially when shootout results showed that OCaml was sometimes faster than C, and when I discovered that OCaml was much faster than Hasell, I was really starting to think that OCaml was a possibility.

      I put a link to tho OCaml mailing lists above. Use it. Ask questions of the list (you may want to start with the beginner's list). They can help you learn the language faster and better than google will.

      However, the ONLY reason why I would want to use OCaml is to take advantage of the expressiveness of pure functional programmin

      --
      -30-
    3. Re:Email response by Fourier · · Score: 3, Interesting
      So... maybe you can re-write higher-order memoization code using more efficient data structures? I would love to see that code, and I'm sure the OCaml community would benefit from having that in their toolbox.

      You get a significant boost just by dumping the list memoization in favor of a hashtable implementation. I'm not necessarily saying that's the optimal choice, but it's an easy drop-in replacement that is much better suited to the task. Here's a patch:
      --- Garden.ml 2005-03-14 13:22:04.000000000 -0500
      +++ Garden2.ml 2005-03-15 14:38:34.000000000 -0500
      @@ -135,8 +135,8 @@ let costList = map cost allStates;;

      (* Set up an associative list for memoization *)
      -let lookup key table = List.assoc key !table;;
      -let insert key value table = table := (key, value) :: !table;;
      +let lookup key table = Hashtbl.find table key;;
      +let insert key value table = Hashtbl.add table key value;;

      (* memoize any 3-parameter function *)
      @@ -150,7 +150,7 @@ let memoize3 table f x y z =
      result;;

      (* table for memoizing optLayout *)
      -let isCovered_table = ref [];;
      +let isCovered_table = Hashtbl.create 100;;

      (* checks if each cell in center colum is covered by an empty cell *)
      let rec isCovered c1 c2 c3 =
      @@ -266,7 +266,7 @@ and memo_fib n = memoize fib n;;
      *)

      (* table for memoizing optLayout *)
      -let optLayout_table = ref [];;
      +let optLayout_table = Hashtbl.create 100;;

      (*
      Also: learn to use the profiler! It takes about five seconds to see that camlList__assoc is killing you.
    4. Re:Email response by Tom7 · · Score: 1

      Production ML code sure does use lists, and they are efficient for what they are. But if you want fast random access, lists are simply not the right data structure. (And you don't even necessarily need to resort to imperative structures to get this; functional splay trees or functional arrays can get you at least good asymptotic performance, if not good constants!)

      Anyway, map and fold are available for arrays as well as lists.

    5. Re:Email response by Anonymous Coward · · Score: 0
      Every single example that shows how elegant Haskell and OCaml are uses lists. The 4-line Quicksort example for Haskell uses lists.

      But sorting is very different from memoization. Sorting algorithms in general don't require random access to the elements that are being sorted, but they do require to produce an ordered sequence as a result. The key property of a linked list is that it's accessed sequentially, starting from the head.

      Guess what? A lot of algorithms do just that: traverse a bunch of elements one by one in a given order, and at each step, work only with the current one, or maybe the current one and some of its successors. Nearly all of the world's for loops are some variant of this pattern. (And fold is nothing but the functional abstraction of a for loop.)

      Memoization involves retrieving an unique element for a collection given a key. This kind of data structure is called a dictionary or a map, and the most efficient way of doing this requires random access, which makes it O(1) time over the size of the map. The next most efficient way of doing it involves using various kinds of tree-like structures, which are usually O(log n) over the size of the map. One shouldn't discount the costs imposed by the key, and by hit/miss ratio: a hash table, for example, has a cost for computing the key, which goes up with the size of the key, and is incurred for all operations; a trie (a kind of tree-based dictionary) often has cheaper costs for complex keys, because it can often detect that the map doesn't have the key without looking at the whole key.

      Association lists are a pessimal implementation (O(n) over the size of the map), but they're quick and dirty, so they're often used when the maps involved are small and don't change frequently; they do have a nice property, that you can treat them like a stack, and push an association on the top, which will shadow other entries with the same key, and then pop it off, which restores the previous association.

    6. Re:Email response by rjshields · · Score: 1
      Great response. IMHO the article and jcr13 should be modded "-1 fuckwit". He has drawn some very wrong conclusions from his flawed experiment.
      The "falsehood" here is that you claim that code written in a functional style is slow, when you really should have said "my code written in a naively functional style is slow"
      Quite so. If only he realised.
      --
      In this world nothing is certain but death, taxes and flawed car analogies.
  25. More sensible results. by Caine · · Score: 1

    Look at the shoot-out with only tests that are all available for C, C++ and O'Caml are included and weighted for both CPU and Memory. It paints quite another picture.

    1. Re:More sensible results. by Caine · · Score: 1

      Look at the shoot-out with only tests that are all available for C, C++ and O'Caml are included and weighted for both CPU and Memory. It paints quite another picture.

      Hm. Slashcode messed with the URL. Use good old copy and paste:

      http://shootout.alioth.debian.org/great/benchmar k. php?test=all&lang=all&sort=fullcpu&xfullcpu=1&xmem =1&xloc=0&ackermann=1&wc=3&echo=5&fannkuch=0&fasta =0&fibo=1&harmonic=0&heapsort=4&knucleotide=0&mand elbrot=0&matrix=3&nbody=0&nsieve=0&nsievebits=0&ob jinst=5&methcall=5&pidigits=0&random=3&regexmatch= 0&revcomp=0&reversefile=4&spellcheck=0&hello=0&mom ents=0&sumcol=0&takfp=1&tcpecho=0&tcprequest=0&tcp stream=0&process=0&message=0&wordfreq=5

    2. Re:More sensible results. by Fahrenheit+450 · · Score: 1

      And if you weight all tests evenly, rather than giving some more importance than others you get an even different picture...

      Also with cut and paste goodness (mind the injected spaces)...

      http://shootout.alioth.debian.org/great/benchmar k. php?test=all&lang=all&sort=fullcpu&xfullcpu=1&xmem =1&xloc=0&ackermann=1&wc=1&echo=1&fannkuch=0&fasta =0&fibo=1&harmonic=0&heapsort=1&knucleotide=0&mand elbrot=0&matrix=1&nbody=0&nsieve=0&nsievebits=0&ob jinst=1&methcall=1&pidigits=0&random=1&regexmatch= 0&revcomp=0&reversefile=1&spellcheck=0&hello=0&mom ents=0&sumcol=0&takfp=1&tcpecho=0&tcprequest=0&tcp stream=0&process=0&message=0&wordfreq=1

      --
      -30-
  26. moot by pinka · · Score: 1

    moot actually means debatable. However, in the expression "moot point" it seems to be used in the sense "tangential/unnecessary point"

    1. Re:moot by circusboy · · Score: 1

      I suppose that would explain why it was called an 'Entmoot"...

      --
      -- it's ridiculous how many people misspell ridiculous... (damn, damn, damn...)
  27. python is the fastest language by kayumi · · Score: 0
    I don't know what all the fuss is about. I have written python programs which ran faster than their C counterparts. And I am sure someone else can prove similarly that awk is even faster.

    It all boils down to understanding the complexity of the basic building blocks of your algorithms which can vary considerably with the data structures you choose to implement them.

  28. update the web page? by mjsottile77 · · Score: 2, Insightful

    Have you considered updating that web page - keep the content you have there now, but possibly tack on some of the feedback from here that shows that yes, a very, very bad implementation of an algorithm will run slowly, regardless of the language. It would be a shame of someone ran across the page seeking a realistic comparison, and didn't look deep enough to realize that this "study" is basically the equivalent of comparing the gas mileage of two makes of automobile without informing the readers that one car had a semi-truck chained to the back of it during the trials.

    1. Re:update the web page? by Anonymous Coward · · Score: 0

      ...Or in this case, a driver who didn't know that you can actually shift out of first gear.

  29. Quick Haskell Rebuttal by Anonymous Coward · · Score: 3, Interesting
    This is a 10 minute proof-of-concept that Haskell shouldn't lag as much as claimed. It's hardwired for n*3 grids, doesn't use memoization or arrays, and it solves 7*3 in 15 seconds on my ancient hardware. 15 lines of code, not astoundingly elegant, but no optimization tricks at all. If anyone cares I will write a generalized version to kick C++'s arse later.
    import Word; import Bits; import List

    collength = 7
    full = 2^collength-1

    selfs c = (shiftR c 1) .|. c .|. ((shiftL c 1).&.full)

    invert c = foldl (.) id [if(testBit c i)then(flip setBit (collength-1-i))else id|i<-[0..collength-1]]
    0

    empties c = length [()|i<-[0..collength-1],testBit c i]

    valids = [((c1,c2,c3),e1+e2+e3)
    |(c1,s1,i1,e1)<-c's, (c2,s2,i2,e2)<-c's, (c3,s3,i3,e3)<-c's,
    c1==minimum[c1,i1,c3,i3],
    s1.|.c2==full, c1.|.s2.|.c3==full, c2.|.s3==full]
    where c's = zip4 cs (map selfs cs) (map invert cs) (map empties cs)
    cs = [(0::Word32)..full]

    bests = (best,[cs|(cs,score)<-valids,score==best])
    where (_,scores) = unzip valids
    best = minimum scores
    1. Re:Quick Haskell Rebuttal by Anonymous Coward · · Score: 0

      Sorry, the 15 seconds was using ghci, if I compile it with GHC with no options it takes half that time.

    2. Re:Quick Haskell Rebuttal by 3dWarlord · · Score: 0

      Memoization puts the "lazy" in Haskell. It is not true to claim you do not use it,

    3. Re:Quick Haskell Rebuttal by KoporShow · · Score: 1

      Memoization and lazyness are basically different concepts.

    4. Re:Quick Haskell Rebuttal by mcbevin · · Score: 1
      You guys really do live in another world don't you?

      10 lines of code ... but do you seriously thing such style makes for readability and maintainability?

      invert c = foldl (.) id [if(testBit c i)then(flip setBit (collength-1-i))else id|i&lt;-[0..collength-1]]
      I suppose this example from your code is one line right? Someone tell me please whether this kind of mess is standard Haskell coding practice. Because to me it looks like theres a bunch of different things going on in that line, that would be better readable spread over a few lines with a bit of indentation, a few comments, sensible variable names etc. Or is it just my non-functional-programming ignorance that makes me think this way?

      You people go on about how programming is an 'art', and how us non-functional C++/C#/Java programmers don't know that 'art;. Personally I'm open to the advantages of functional programming, and regret that I never took the paper on it at Uni which was offered, but if cramming your program into 15 lines of unreadable, unmaintainable code counts as 'art' then I'll leave the artistry to the academics and get on with my job of creating real solutions to real problems. .

    5. Re:Quick Haskell Rebuttal by mindboggler · · Score: 1


      No, IMHO its just bad style.

      I think the author of this haskell code chose to write short and fast code when speed, readability, conciseness in that order perhaps would have been a more apropriate set of priorities in the context of the cited article.

      After all, the computational costs of additional comments and descriptive name are astonishingly low in most programming languages.

      On the bright side, the code is considerably fast (it takes 2 secs on a 1.3 GHz pb with ghc-6.4 -O) and could easily be made readable.

      So please don't settle your opinion on fp because of this - there's a lot of beautiful functional code out there worth being explored.

    6. Re:Quick Haskell Rebuttal by Fahrenheit+450 · · Score: 1
      I suppose this example from your code is one line right? Someone tell me please whether this kind of mess is standard Haskell coding practice. Because to me it looks like theres a bunch of different things going on in that line, that would be better readable spread over a few lines with a bit of indentation, a few comments, sensible variable names etc. Or is it just my non-functional-programming ignorance that makes me think this way?

      It's not really all that bad (though I would have put a couple of newlines in there...) let's look at what it does (Word of warning: I'm sure I'm going to screw up on something minor below...).

      invert c =
      foldl (.) id
      [if(testBit c i)then(flip setBit (collength-1-i))else id|i<-[0..collength-1]]
      0

      Let's deconstruct it, shall we?

      invert c = : This creates a function named invert with a parameter c and a body defined by what follows.

      foldl (.) id [list description] 0 : foldl "folds up" a list, here that long thing we'll discuss below, using a binary operator, in this case (.) we'll get to that soon, and a base value, here id, and it does so in a left associative manner. That is to say, given a base value b, an operator, op, and a list [l1, l2, l3, .., ln], we compute ((((l1 op l2) op l3)...) op l(n-1)) op ln). We then take the result of this folding and apply it to zero (so we know that the result of the folding is a function that takes an integer argument).

      (.): This is the composition operator, it composes two functions. That is to say (f.g) x = f (g x).

      id: This is the identity function, so id x = x.

      Put it all together (assuming for the moment that the big ol' listy thing is a list of functions, [f1, f2, f3, ..., fn]), and we see that we are computing the function (f1 (f2 (f3 (... (fn (id 0)))))), or more correctly, computing the function f x = (f1 (f2 (f3 (... (fn (id x)))))) and applying it to 0 (which in this case represents an all zero bit-vector, the all empty state). It ends up being the same thing...

      OK, so what functions are we composing here? Well, lets look at the expression...

      [if(testBit c i)then(flip setBit (collength-1-i))else id|i&lt;-[0..collength-1]]

      First of all, this is a list comprehension. It essentially says "take the expression to the left of the | and give me one of those for every value on the right of the |", in this case for each value i in the range 0 to collength - 1. So what does the expression on the left say? Well, first, it checks to see if the ith bit of the bit-vector c, (again just another representation of an integer) is set -- that's the if(testBit c i) part. If it is set, then we return the function (flip setBit (collength-1-i)), which we'll explain in a second, and if the ith bit of c is not set, then we just return the identity function. Now, just to finish up, (flip setBit (collength-1-i)) evaluates to a function that sets the (collength-1-i))th bit of the bit-vector that will be passed into the function. We need the flip, because setBit is defined to take the bit-vector as its first argument, then the position of the bit to set. However, we don't have the bit vector yet -- that's what we will be applying our resulting function to -- so we need to "flip" the order of the arguments to setBit. Note that flip f x y = f y x.

      So all together, we are creating a function that, given a bitvector c, will start with an empty bit-vector (call it z) and checks if the (collength-1)st bit of c is set. If so, it flips bit 0 of z, otherwise it leaves z alone, cal

      --
      -30-
    7. Re:Quick Haskell Rebuttal by Fahrenheit+450 · · Score: 1

      Gerf... I knew I'd screw something up.

      This:
      That is to say, given a base value b, an operator, op, and a list [l1, l2, l3, .., ln], we compute ((((l1 op l2) op l3)...) op l(n-1)) op ln).

      Should be:
      That is to say, given a base value b, an operator, op, and a list [l1, l2, l3, .., ln], we compute
      (((((b op l1) op l2) op l3)...) op l(n-1)) op ln).

      And consequently this:
      (f1 (f2 (f3 (... (fn (id 0))))))), or more correctly, computing the function f x = (f1 (f2 (f3 (... (fn (id x))))))) and applying it to 0

      Should be:
      (id (f1 (f2 (f3 (... (fn 0)))))), or more correctly, computing the function
      f x = (id (f1 (f2 (f3 (... (fn (id x)))))) and applying it to 0.

      Looking for other screw-ups is left as an exercise to the reader...

      --
      -30-
    8. Re:Quick Haskell Rebuttal by Anonymous Coward · · Score: 0

      That was what I meant by "10 minute proof of concept" and "not elegant". What made you think this was supposed to resemble production code? And anyway, it's such a _small_ mess compared to the C++ and OCAML versions, that it still wouldn't take anywhere near as long to read.

    9. Re:Quick Haskell Rebuttal by Anonymous Coward · · Score: 0

      Man, I don't know why people like you bother at all. This is a hopeless discussion, and you come like that explaining Haskell to the masses?

      I wish I was like you, but I think I'm just too cynic nowadays...

      Sir, I salute you.

    10. Re:Quick Haskell Rebuttal by mcbevin · · Score: 1

      I understood that it wasn't aimed at being production code, but from my experience what starts out as a '10 minute proof of concept' usually ends up being the production code. Personally even for 10 minute proofs of concepts which I never expect to touch again I find its always worth paying attention to some level of intelligent variable names etc.

      I'm also sure its much smaller than any C++ version (and it could presumably be still much smaller while being made much more readable). However whether its more readable (even to someone well experienced in both paradigms) is perhaps debatable. I occasionally use regular expressions, which also result in much smaller messes compared to say the non-regular-expression C++/Java/C# code required to achieve the same thing, but I find that even with some experience in regular expressions they generally take longer to comprehend that the equivalent (albiet longer) non-regular-expression code, and anything non-trivial requires a lot of comments regarding what each part aims intend if it is to be maintainable.

    11. Re:Quick Haskell Rebuttal by Anonymous Coward · · Score: 0
      Or is it just my non-functional-programming ignorance that makes me think this way?
      Yes. I'll grant you the variable names are bad, but other than that, it is 23 times less code than the C++ version. That means the typical 1,000,000 lines-of-code project could be done in 43,000. Take a guess at which one has fewer bugs and took less time to develop.
  30. I appreciate your predicament by Paradox · · Score: 0

    jcr13, I feel your pain. I've hit the exact same wall and bounced in nearly the same fashion.

    There are tons of examples of OCaml code out there, but whenever you try and use them you'll find they're written for elegance, not for any real-world metric. It makes for good propaganda, but not much help for anyone trying to do anything real. Asking for help and showing this code in most communities results in a series of curt, bitter responses from many members of the community.

    So even though all kinds of arrogant Ocaml and functional programmers are coming after yor for "spreading lies", don't despair. You're not the only one to make this observation about the community.

    --
    Slashdot. It's Not For Common Sense
  31. Polymorphism has its moments... by Anonymous+Brave+Guy · · Score: 1

    The insert example isn't great. Despite using the same name and similar end results, these are unlikely to have the same performance characteristics, which is likely to be a significant difference in practice.

    However, consider a simple function such as map, which fundamentally takes some structured data, and produces a new set of data with the same structure, built from the original elements acted on by some function. This concept is applicable to a wide range of data structures, and writing it polymorphically (not generically) just makes for code that's easier to read and less memory work for the developer.

    A more advanced example might be the use of a fold style function directly on lists. A similar concept could be applied to a tree, but you'd have to have some intermediate functionality that defined the ordering you were using to traverse your tree. Still, it can be useful to separate the concepts of the ordered algorithm (fold, or whatever more complex function is using it) from the ordering algorithm (traversing breadth-first, depth-first, etc.), from the connecting and terminal functions used by the fold algorithm specifically but potentially useful elsewhere as well.

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
  32. OCaml Evalation Is Strict, Not Lazy by j+h+woodyatt · · Score: 3, Informative

    If you want to program in a functional style, and you need lazy evaluation, you're going to find the standard library that comes with the compiler somewhat limited.

    I wrote some extensions for programming in OCaml in the functional style. Check out the OCaml NAE project, and look for the Core Foundation (Cf) package.

    --
    jhw
  33. Algebraic Dynamic Programming by Anonymous Coward · · Score: 0

    Actually, it is possible to do this. Search for "Algebraic Dynamic Programming" to find some papers describing this. Basically, you specify your problem as a grammar similar to BNF, and supply an evaluation algebra - and DP is added by a simple annotation indicating which parts of the parser should be tabulated. All implemented directly in Haskell.

    -kzm <ketil at ii dot uib dot no>

  34. The effect of community by Anonymous+Brave+Guy · · Score: 2, Insightful
    Asking for help and showing this code in most communities results in a series of curt, bitter responses from many members of the community.

    That's a very important point, and I think the effect of the community around a programming language (or indeed a whole paradigm -- functional, OOP, etc.) is often underestimated.

    Just look at PERL: it has its merits, but in fairness clarity to newcomers is rarely listed as one of them. And yet, because asking a question on a PERL group tends to result in joyous cries of "TMTOWTDI" followed by an enthusiastic discussion of the relative merits of 17 different ways to solve your problem, even the most unaware newbie is likely to find help and education simply by asking politely.

    In contrast, some languages (C and C++, for example) tend to invite technical perfectionists, who are happy to help but only if you're Doing Things Properly(TM). Others (functional languages, LISP dialects, etc.) tend to have quite academic communities, whose response to newbies, sadly, can be less than welcoming.

    Now, take a look at those languages I listed, and ask yourself which ones have been the bigger success stories...

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
  35. Wrong discussion issue by Anonymous Coward · · Score: 0

    I am thinking that it very wrong to discuss about language itself. Discussion should be about it's implementation.
    As ratboy666 showed example, implementation can be very crucial.

    Everyone knows that no usage is better than wrong usage. Andrew Koeing said in C Traps And Pitfalls for C: "The C language is like a carving knife: simple, sharp, and extremely useful in skilled hands. Like any sharp tool, C can injure people who don't know how to handle it."

    The same is for C++, OCaml, assembler etc. If you are skilled enough, for examaple in perl, you will do faster things than many people in other languages, even assembler.

    For STL library; many people use it wrong. They even don't read documentation about it. I mean good documentation. For example, every book about C++ will show only 10% of STL, but deep secrets are only findable in advanced papers.

    Look this example:
    1) string s = "test";
    2) string s("test");

    For ordinary user, there is no difference and many books don't discuss this small issues. Only books from language experts will explicitly note differences (Effective C++ series, Exceptional C++ series etc) and show that 1) example will do 3 calls, but 2) will do only once. Of course, good implementation will optimize 1) to be as same sa 2), but that is another issue.

    Also users should be very careful even for STL implementation. STLport will do much faster string handling, than libstdc++ from gcc. Why ? The only solution is to fire up your editor and go deep in implementation itself. Results will be self-explainatory.

    So, at the end. Use good books (one good book, even harder for reading, is better than ten bad), and test implementations.

  36. The Common Lisp community is pretty helpful by Szplug · · Score: 1

    Check out comp.lang.lisp to see for yourself. No one appreciates people asking them to do their work for them but, they're helpful to people earnestly trying to learn.

    --
    Someday we'll all be negroes
  37. Haskell version by sleepingsquirrel · · Score: 1

    Here's my first attempt at a version which performs decent in Haskell. It doesn't do any memoization, and isn't particularily dynamic. It ended up being only 35 non-blank lines of code. The 9x3 case takes 26 seconds to complete on my Athlon 1600 compiled with GHC 6.4. Erg. You'll have to grab a copy here as /. thinks my Haskell code has too many 'junk' characters.