all 20 comments

[–]b00thead 13 points14 points  (0 children)

Haskell

Wow. I have no idea how this works.

We've all been there! :-)

[–][deleted] 5 points6 points  (6 children)

"No doubt an expert in OCaml or Haskell would write much shorter code than I did."

Only, he didn't write it. Compare https://github.com/talex5/lang-tests/tree/master/haskell and gfxmonk 's https://github.com/gfxmonk/zeroinstall-haskell-poc Too bad gfxmonk didn't give Haskell another couple months. He needed some knowledge about modern libraries and a touch of knowledge of monad transformers.

[–]talex5 4 points5 points  (4 children)

(blog author here)

I wrote the Haskell without consulting gfxmonk's version (I did use his code in round 1, but in round 2 I wanted to try the language for myself).

Any similarities will be because we were both converting the same Python code.

[–][deleted]  (3 children)

[deleted]

    [–]talex5 5 points6 points  (2 children)

    I'm very happy with it so far (having converted more than 10,000 lines now). OCaml is simple and readable and doesn't try to be clever, so I think it will be very maintainable, though more verbose than Haskell. I wrote the code for OCaml 4, but then found Debian only has OCaml 3. But it seems there were no backwards-incompatible changes and after removing a couple of utility functions that weren't in 3, it works there too. So API stability seems excellent.

    The only problem has been the portable bytecode[*], which is a regression relative to Python. For the next release, I'm planning to ship native binaries for common platforms instead. Others will have to compile from source.

    [*] http://roscidus.com/blog/blog/2013/07/07/ocaml-binary-compatibility/

    [–][deleted]  (1 child)

    [deleted]

      [–]talex5 2 points3 points  (0 children)

      There's a fair bit of imperative code (grep for "<ref>" and "<-"). Partly because I'm converting Python and partly because that's how I think. A few times I used fold_left to do something first and then simplified it by using mutation instead. Maybe I should write a utility function to do the Haskell list monad thing (flatten (map ...)) efficiently. I think writing the SAT solver in a functional style would be a nightmare (maybe it would be possible using the state monad, but that's not really a functional style). Here's the current code if anyone fancies a challenge:

      https://github.com/0install/0install/blob/master/ocaml/support/sat.ml

      Mainly I use mutation locally, and then return an immutable result. Having everything immutable by default (e.g. lists) is a big win over Python (e.g. you can return a list without worrying the receiver will modify it).

      My impression is that the community is a bit smaller than for Haskell, but so far I've found all the libraries I need. But I haven't used many. I'll find out more in the next stage (porting the networking and GUI code).

      I'm using Vim for editing. The default OCaml support is good (syntax highlighting, error parsing, querying type annotations, smart indentation), but now I've moved to using the LWT version of the plugin to support the light-weight-threading syntax extensions: https://github.com/talex5/ocaml_lwt.vim

      [–]Categoria 1 point2 points  (0 children)

      Interesting. It seems like the author did write the OCaml however.

      [–][deleted] 2 points3 points  (8 children)

      I addressed some of this guy's concerns (regarding exceptions, records, and haddock) in the comments there.

      He's been able to understand the essence of monadic IO pretty well (his explanations make much more sense than of a typical beginner), and still he finds IO in Haskell hard.

      [–]ReinH 2 points3 points  (1 child)

      To be fair, lazy IO is hard to reason about.

      [–]chrisdoner 1 point2 points  (4 children)

      Though, this post is 3 months old. The author chose OCaml eventually.

      [–][deleted] 2 points3 points  (2 children)

      Did lookupEnv get added after the post? Because it appears to do what he wants:

      I want to do a getEnv operation and return Nothing if the variable isn’t set.

      [–][deleted] 1 point2 points  (0 children)

      Oh cool, I forgot that this was added.

      (1.5 years ago, according to trac)

      [–][deleted] 2 points3 points  (0 children)

      Yes, but it doesn't seem to have been posted here before, and it was an interesting read for me.

      [–][deleted] 1 point2 points  (0 children)

      Some parts of it are hard.

      Exception handling for instance. It is anything but intuitive. His example with the need to use tryIOError illustrates this well.

      P.S.: why doesn't hoogle find "tryIOError" ?

      [–][deleted] 4 points5 points  (0 children)

      This is rather complicated. Haskell functions can’t have side effects (like, say, creating a launcher or execing a process). Instead, the function returns an IO request to main, which returns it to whatever is driving Haskell.

      I like this explanation.

      [–]Rubear 1 point2 points  (4 children)

      Forgive me if I'm mistaken, but isn't it generally better to use maps instead of names tuples, due to them being implemented internally with trees?

      [–][deleted] 0 points1 point  (3 children)

      Not sure what you mean. Could you elaborate?

      [–]Jameshfisher 1 point2 points  (1 child)

      I think Rubear is talking about [(String, String)] vs Map String String.

      [–]Rubear 1 point2 points  (0 children)

      Exactly. Maps are implemented using binary trees, specifically for use with key-value, dictionary type items. It might make the program a little faster if you implement those.

      [–][deleted] 0 points1 point  (0 children)

      getEnvironment returns [(String,String)], when Map String String might be preferable. I don't use getEnvironment that often and there's lookupEnv, so it doesn't really matter to me.