Hacker News new | ask | show | jobs
by thoughtpolice 3906 days ago
Haskell does not eliminate side effects. It allows you to control them.

You do realize that system you described - Facebook's Sigma - is a real-time, massively concurrent online spam-fighting system, that automatically and implicitly makes your code concurrent for all operations, can optimize it, and has automated caching/batching request mechanisms in place to talk to external systems (Graph databases, SQL, cache servers) in an optimal way while reducing things like round tripping, with naive code? (e.g. it can automagically parallelize and optimize the "N+1 Query Problem" into an efficient query that minimizes duplicated requests and round trips, with no intervention, and the single 'query' can operate over multiple data sources like mentioned previously.) In other words: it does side effects all the time, and makes them manageable in ways you could only dream of in other languages. Did I mention all of that was done in a library? :)

I mean, we definitely have a sales problem, don't get me wrong. We could spit these stories better or clean up the presentation. I can list off a dozen things Haskell is poor at, or probably an infinite number given the time. But you sort of chose literally the worst example in the world to make your point.

I would suggest you actually spend some time with the language in anger. It is not easy. But I've been writing 'real world' software in Haskell for years, and it's no more difficult or 'magical' than any other part of being a software developer. It's just more rewarding because my code works far more often. And working code seems to be something that's rare in the software world these days ;)

2 comments

Thank you!

That's a great example of a challenging system, analogous to things I've worked on in the past, so I can feel the complexity, and your description also helps me sense the way Haskell is well suited to the task.

So that's cool. I'm glad to hear it. The best example found to date.

And you gotta admit, crdoconnor has a good point that the impact on the world at large may increase as lessons learned in Haskell spill over into the Lesser Languages.

I still hope that some of these super smart guys would take a short break from the language wars. And briefly at least, consider working on things with a more tangible outcome.

You misunderstood me. I wasn't saying that it eliminated side effects. I was saying that it brought no great advances with respect to side effects, and most people write code that mostly only does side effects. This is why OO had such a great impact on the business world and functional programming didn't.

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.

>I would suggest you actually spend some time with the language in anger. It is not easy. But I've been writing 'real world' software in Haskell for years, and it's no more difficult or 'magical' than any other part of being a software developer. It's just more rewarding because my code works far more often. And working code seems to be something that's rare in the software world these days

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.

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

Stable, working code is rare, I'll grant you that, and weaker type systems definitely make programming more of a balancing act, but there's a trade off to be made between stronger type systems and not having to rewrite a ton of working code which already exists.

> I was saying that it brought no great advances with respect to side effects,

Really? I work with a bunch of languages that does not have controlled side effects, and controlled side effects is the single biggest thing I miss from Haskell. The uncontrolled side effects I swear over daily, if not hourly.

The fact that the order in which I call functions can – undocumentedly – decide whether my program works or crashes is a ridiculous concept. The fact that if I share the wrong piece of data with another part of the application my program starts behaving erratically from having invalid data is odd.

Sharing data should not feel unsafe. Changing the order of method calls should not be a threat.

As long as a function gets the data it wants through explicit parameters, it should work. It shouldn't matter at which point in time I call it, because managing time is really difficult. Managing data is easy.

The controlled side effects in Haskell give you a way to encode these things to give you guarantees and self-documenting code. It gives you opportunities to deal with these problems in sane ways.

Refactoring Haskell code is mostly a matter of symbolic manipulation. A huge chunk of the process doesn't even require me to think about what I'm doing, because I have much more freedom when I don't have to worry about side effects myself. This is good, because I'm bad at thinking and I would like to do it as little as possible. Every single bug that have appeared in my problems have been because I'm bad at thinking.

> 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.

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.