Hacker News new | ask | show | jobs
by thoughtpolice 3907 days ago
> I was saying that it brought no great advances with respect to side effects,

Sure it does. Well, side effects are boring. It's how you actually do the dance that matters. Here's a few:

- Haskell has a best-in-class concurrency story, with a far greater breadth and depth of techniques and libraries available in most languages (and some, like STM, are realistically impossible otherwise). These include many manners of graph, dataflow, (implicitly auto) parallel and concurrent programming.

- We have a nice blend of epoll based event loops with a multicore runtime, all compiled to native code. This means the traditional 'one thread per client model' is as efficient as any epoll/event loop solution. In practice, it is so cheap because Haskell threads are so cheap that it completely changes how you use concurrency, as it is almost free. It is cheap and easy to add, and the aforementioned toolbox makes it easy to do things - e.g. the `MVar` acts as a one-place blocking queue, which you can use as a lock or shared data structure, as you see fit, between threads.

- Haskell's type system has many features most other 'side effectful languages' don't offer, and even ones that are completely impossible even in things like dynamic languages.

For example, it's possible to use the type system to do things like ensure file handles are tracked statically so they cannot escape a certain scope (a form of 'region management', tracked at compile time). This is actually not even that difficult, honestly.

- Similarly, things like type classes offer a powerful set of abstractions for working with 'effectful code', and allows us to do things like easily separate interfaces from implementation. I can specify a 'Redis' type class and then a concrete implementation of this 'Redis' class, which may or may not be a fake or real. Then I can write functions parameterized only over this type class, and it works with both, and I choose which to use. If I push this further with more classes, individual functions may have a 'Redis' context and a 'MySQL' context to do multiple things; or it may only have a 'Redis' context, and no MySQL. This can be used to enforce separation.

- Lots of use of the type system to enforce all kinds of special cases and invariants. For example, it is entirely possible to use the type system to avoid things like SQL or XSS injection attacks, as done in Yesod. For the extreme version of this, the Ur/Web language takes it to the ultimate conclusion.

- Similarly, it's very easy to encode business logic in Haskell types. You can ensure things like you never mix up a "CustomerID" value with a "ClientID" value, which are really both Ints, but you can make the compiler yell when you mix them up or get something wrong.

- Haskell is a lazy language, meaning it is very easy to refactor, even I/O code. That is because you can pull any piece of code out into a name. Consider this program:

      if thing then error "bad things" else 10*2
I can change this to:

      let x = error "bad things" if thing then x else 10*2
You cannot do this in a non lazy language. Well, you can, but it gets miserable pretty quick. Furthermore this applies to any subexpression; suppose I have a program:

      h <- openThing "foo"
      doStuff h
      ... some more stuff ...
      undoStuff h
      closeThing h

   I can change this to:

      let begin f =
        h <- openThing f
        doStuff h
      let end h =
        undoStuff h
        closeThing h

      h <- begin "foo"
      ... some more stuff ...
      end h
Again, this is not valid if your language is strict. But these patterns are very obvious and common when you can freely rearrange any expression. Then you can see the final abstraction easily:

      let withThing x f = 
        h <- openThing f
        doStuff h
        f
        undoStuff h
        closeThing h

      let stuff = ... do some stuff ...

      withThing "foo" stuff
This particular point requires a lot of experience to truly appreciate in practice IMO. But in practice it means you can freely move code anywhere at no cost, even any I/O code, so you can very easily abstract over it like above. In non-lazy languages, this becomes significantly more tedious.

> It's actually pretty rare for businesses to write highly complex mostly functional code (a spam filter is one exception), and that is where Haskell really shines.

The term 'mostly functional code' is meaningless, because we have not even defined what it means, so it's not really useful for me to try and convince you of anything :)

Again, you do realize the 'spam filter' that Facebook produced isn't just some weekend Bayesian spam filter, right? Actual software at the scale is quite complex. I'd assume it handles at minimum tens to hundreds of millions of requests a day and integrates with millions of lines of internal code. I'm probably lowballing that, even. It's a serious piece of software.

> I've tried it and I've seen its advantages and I'm content to remain with python for the time being. Python's type system isn't as good and that does sometimes cause bugs I wouldn't get in Haskell which I fully understand, but I'd consider the difference incremental. It's not any kind of great leap forward and I think my code would only be slightly less buggy in equivalent Haskell.

That's fine. But based on what I've seen (to be completely honest, not as an insult, you do not give the impression of someone who has spent extensive time with it[1]), I'd say you're greatly underestimating exactly what it is capable of, as well as precisely what capabilities it offers in the first place.

[1] The fact it should take you so long to understand it is a problem in its own way, and we should certainly keep seeking to narrow the gap between "regular ordinary programmer person" and "someone with a weird disposition for Haskell"-slash-"Someone who has way too much time"-slash-"Someone who is super persistent". Totally a problem.

> On the other hand, Python has a wealth of packages and an ecosystem that Haskell simply doesn't even come close to matching.

This is certainly a real complaint for a lot of domains. It's one of the many things I could complain about. In other domains, Haskell is excellent and has quite a lot of good options. It seems to be particularly popular for web developers these days. It's definitely still pretty terrible if you want like, QT bindings on Windows.

1 comments

The concurrency stuff sounds nice. Apart from that I'm underwhelmed.

I can handle epoll based code without problems anyway.

The whole "file handles" can't escape a certain scope sounds like what python's with statement does.

The whole it's amazing that you can "encode business logic in types" thing I really don't get. That's basically the selling point of object oriented code.

Similarly, SQL injection is a problem that largely plagues dumb PHP code and is close to irrelevant if you use a half-way decent ORM in any language.

>The term 'mostly functional code' is meaningless

Any code that is mostly made up of functional programming, but still has some state handling code. I think it's pretty clear.

>Actual software at the scale is quite complex.

Gee maybe that's why I said it was complex.

>That's fine. But based on what I've seen (to be completely honest, not as an insult, you do not give the impression of someone who has spent extensive time with it[1]), I'd say you're greatly underestimating exactly what it is capable of, as well as precisely what capabilities it offers in the first place.

No, I haven't used it extensively. I've played with it.

I honestly think if it the benefits you tout were as great as you seem to think they are then there would be a far greater wealth of practical software written in it.

I know of one website (a local fashion retailer) and Facebook's spam filter. That's about it.

> The whole "file handles" can't escape a certain scope sounds like what python's with statement does.

It is incredibly easy to return a file handle out of a with statement. There is nothing in place other than programmer discipline preventing you from doing so, and your program blows up in... interesting ways.

> The whole it's amazing that you can "encode business logic in types" thing I really don't get. That's basically the selling point of object oriented code.

With OOP languages those are mostly runtime errors rather than compile time errors. Encoding logic into the type system means that many application logic errors will be caught at compile time before your application goes into production.

> I honestly think if it the benefits you tout were as great as you seem to think they are then there would be a far greater wealth of practical software written in it.

I honestly think if PHP were as bad as it seems there would be far less practical software written in it. Popularity doesn't imply quality just like lack of popularity doesn't imply lack of quality.