1. Can you introduce yourself and tell us what you’ve been working on lately?
John Hughes: My background is I’ve been functional programming researcher since about 1980 and I’m a professor at Chalmers University in Sweden and also founder of Cubic. Most recently I’ve been working on a tool called QuickCheck for software testing.
Simon Thompson: I’m a relative youngster; I’ve been doing functional programming research since 1983, after doing a PhD in Logic. I’ve done a number of things, but most recently I’ve been working on a tool to do refactoring for Erlang programs.
John Hughes: For me, Erlang was one of the first functional languages to see much industrial use. Then I got excited about the potential for putting all of the ideas that I’ve been working with as a researcher for a long time into real world practice. And I began going to the Erlang user conference and hearing people excited about the same things that I was excited about, but who had been able to use it for real. I found that very exciting.
Simon Thompson: I think Haskell has about it quite an academic community. I did lots of work on developing type systems and so on. Perhaps we’ll talk a little more about this later on. I think that the community has changed a lot in the last 5 years or so. Certainly, 10 years ago, Erlang was the place where people were doing functional programming for real that was the thing that attracted me to start working there. Because you’d hear people who were taking those ideas that we’d been very keen on and using them in practice and that makes things much more exciting than a purely academic environment.
3. Do you use Haskell still? Do you run the compiler sometimes?
John Hughes: Sure.
Simon Thompson: I’m just about to write another edition of my Haskell book. So I’m now completely bilingual. I think I can write Haskell in Erlang and Erlang in Haskell, I can get them really mixed up, but I think now is the time to write another edition. Haskell is at the stage where the libraries for the language are much more stable, there is a more stable release cycle and so on. It’s time to write another edition of that. Certainly I think, given the languages are both functional, it would be hard to find two languages that were more different in a way, they differ in lots of different ways.
Simon Thompson: Particularly the thing I miss in Haskell when working in Erlang is the type system. I think I’m just used when I write a Haskell program, every time I write a function the first thing I do is write down the type of the function. And define the types, have an explicit statement to the types is going to work over. Though that’s now much more possible in Erlang, there still isn’t a standard for specification notations between EDoc Dialyzer, say. I think that’s going to be there in the next release, that’s my understanding. But just being able to say this is the type of this object is something that I miss in Erlang, I guess.
John Hughes: The thing that I miss most when I program with Erlang is lazy evaluation. It’s just such a nuisance not having it! I have delay and force macros which are sprinkled throughout my code and I just can’t live without it.
Simon Thompson: I think this is an interesting debate. There are a number of people I know who said “I would prefer Haskell if it was strict because it’s easier to make a strict program lazy than it is to make a lazy program completely strict.” Now, from what you are saying John, it’s my feeling that it’s harder to get a mental picture of how lazy evaluation is going to work than it is to get a mental picture about how strict evaluation works, then modifying that for laziness.
John Hughes: Understanding what side effects are going to do in a program using lazy evaluation is very difficult. That’s where you really need an operational understanding of what’s going to happen. So you can draw two lessons from that. One lesson is to say “Well, it would be better to have a strict language so that we can reason about our side effects”. The other lesson is “Don’t use the darn things! It’s a bad idea!”
Simon Thompson: But it has much to do with space behavior. It’s harder to predict why does this version of program use 500 MB and this use 10 MB or KB. It’s less modular. It’s less compositional understanding space behavior.
John Hughes: I saw a nice example of that recently. I forget who showed it to me, but the presenter had a little program and there were two alternatives. They said “One of these takes space linear in the running time and the other one it’s more constant space.” It’s hard to understand which one to choose. In a strict language though, they are both bad. I think there are two sides of that space use coin.
The fact is there are more changes to a lazy program to make very dramatic differences to its space use. What’s bad about that is that when you just write the program the first time you are not very likely to get it right. What’s good about it is that when you profile it and you find out where the space is going, you may need only to make a very tiny change in order to improve the space performance measurably.
Simon Thompson: As long as you can work out where that change needs to be.
John Hughes: I think the profiling tools are good for that nowadays.
Simon Thompson: This is my vision. This is maybe where I’m less familiar with what’s the leading edge of Haskell. That’s something that’s never been clear to me, but I know that it’s always been clear to you that it’s been a distinct advantage. There has always been a set of examples where lazy evaluation is the right thing, but for debugging and for doing other things it can be substantially more difficult and it’s that tradeoff. The tradeoff for the whole package that I think is difficult. People still haven’t got debugging right for Haskell.
John Hughes: I find debugging easy, when there are no side effects. You just sit there, you call a function, you look at the results. What’s hard about that?
Simon Thompson: If it’s 500,000 lines of code, if it’s a large program, it’s harder.
Simon Thompson: I think the concurrency model – having that built in right from the start. I think OTP [Open Telecom Platform], those libraries which structure the code in a very particular way, but do that so that you can very quickly build robust systems. I think Haskell also suffers from feature creep. Erlang, the whole release cycle of Erlang is much more disciplined. If you want to build tools, Haskell can be a bit of a nightmare.
If you build tools that will process Haskell ’98 which was the standard (up to a year ago it was the standard), people will say “Wonderful! I would just like this extra-feature that’s GHC [Glasgow Haskell Compiler] support – this one and this one.” In the end, the de facto standard is GHC Haskell and that moves much more quickly, in a much less regular way than the Erlang releases evolve. I think that is a problem for people who want to build commercial systems and it’s a problem for tool builders, potentially.
John Hughes: Sometimes I find the type system such a straight jacket. For example, if you are embedding a domain specific language, which both Haskell and Erlang are good at, when you embed it in Erlang you simply write down syntax trees. For example, we use an Erlang term that says “Call from this module this function with these arguments.” And then we can write a generic interpreter that crawls over that structure and performs all the function calls. It’s very easy to do. Generic programming in Erlang is very easy. Generic programming in Haskell we can still write papers about and in Erlang it’s four lines of code.
Simon Thompson: That was certainly something that we found writing refactoring tools. That’s precisely it! What you are doing there is crawling over the language syntax and in Haskell that’s 30 mutually dependent data types and in Erlang it’s one, which is just the typing terms. Yes, I couldn’t agree more.
John Hughes: Something else that I’m learning to appreciate in the Erlang compiler is parse transforms. I don’t know if you have used them very much.
Simon Thompson: No, not so much. Because we want to is to use source code at the end, so we’ve not been doing that too much.
John Hughes: I think it’s very powerful that you can easily write essentially a compiler plug-in and transform your code however you like for your compiler. If you look at fairly simple transformations, then they are very easy to implement. If you need to extend Erlang just a little bit in some direction, it’s an easy way to do it. Adding a compiler plug-in to GHC would be much harder.
Simon Thompson: GHC has things like rules doesn’t it, further down the chain, but they are not working with source code and I think they are rather more limited.
John Hughes: The great thing about parse transforms is they can do absolutely anything. That’s also the great danger with them. Something that EUnit does for example is that it automatically exports functions whose names end in “Test.” It just adds a new export declaration to the module. That kind of thing makes it easy to make programming convenient.
Simon Thompson: I think F# is interesting because it gives people a very easy way of dipping their toe into the functional water - they could program in C#, they can try things out, they can use the C# libraries and so on. Trying things out helps people adopt systems. When I think in terms of full blown language integration, it’s never quite clear to me that that’s what you want to do. People have played with these things over the years.
There has been lots of work on functional logic languages. I suppose now there is perhaps more of a consensus about where those might go, but they are always going to be a minority interest. When I think what is attractive, it’s the fact that now languages are making it a lot easier to interoperate. There are a number of bindings that allow you to have Erlang interoperating with Java, say, so that within Java you can build something that looks like an Erlang node and have that communicate seamlessly with a real Erlang node.
That’s a nice way to proceed. In Java you do the Java things, in Erlang you do the Erlang things. You got this well defined model for communicating between the two. I’m not sure what you think, John, about that.
John Hughes: I think it’s great to see that there is so much more interest in the field nowadays. Of course with lots more people being interested, lots more good people are doing good stuff. A part of that is we see other new languages arising. As for the language themselves, I think it’s great that languages like C# adopt some of the features of functional programming because it means that C# programmers will then be exposed to those ideas.
I think that will make it much easier for those ideas to spread. Something that I’m asked sometimes is whether I think what’s going to happen to functional programming is that some key features will be put into C++. Then that will be the end of languages like Haskell and Erlang because people will maybe stick with Java, but Java plus a few functional features. I don’t think that’s going to happen.
I think that’s a very good way to educate large numbers of people about the features, but if I make an analogy, when I got interested in programming languages, the dominant programming language was Fortran. I’m sure by today that there are object-oriented versions of Fortran. But if you want to use object-oriented programming, you are not going to go and use Fortran. I see that as a mechanism for spreading the ideas and I think that’s going to result in much more interest in the purely functional languages, in which I count Haskell and Erlang.
Simon Thompson: The semantics are just so complicated, as well, if you do really try and put these things. I can’t imagine what object-oriented programming in Fortran is like. I’m sure it’s there, as you say. But you want to have a clear way of thinking within each language to think functionally within the functional language as you think in Java and put the two together, bridge the two in a clean and simple way. Just because you like jam and you like Marmite it doesn’t mean that jam and Marmite are the thing to put on your bread. They’re both good on their own, but not together.
John Hughes: I thought you were talking about Joe’s abstract machine.
Simon Thompson: I couldn’t possibly comment.
John Hughes: Messaging is about more than I/O, it’s about representing concurrency in a nice way. But I think one of the risks of using Erlang is that it’s so easy to do some I/O and put some side effects anywhere in your code. Haskell makes that more difficult and so it should be. Thinking of my own Erlang code, I normally try and keep things purely functional, but there are some Erlang libraries that, although they could have been implemented purely functionally, have an interface that is stateful.
It’s very tempting then to build your own code on top of those libraries so that that also becomes stateful. I’ve done that and every single bug that has had me tearing my hair out has been caused by those side effects. I think it’s healthy to restrict side effects to a very small part of the program and Haskell certainly encourages that more than Erlang does.
Simon Thompson: I agree. I’m just trying to think what I wanted to say about I/O. What was interesting with Haskell looking back is Haskell tried all sorts of different approaches. It had lazy streams, it had continuations. So it had two different tries and I think some of them were coexisting before it got to monads. Monads are a very nice abstraction. On the other hand, there is something peculiar about the Haskell community. It almost wants to make a millieu of monads. In a way monads are something very simple, but each spring it’s like listening for the first cuckoo spring. It’s the first monad tutorial. I don’t know how many monad tutorials there are out there. There must be hundreds by now.
John Hughes: I think that everybody who learns about them for the first time writes a tutorial.
Simon Thompson: That’s right. Also there are people on the mailing list who are inclined to say “Unless you understand our joint, you need to know this amount of category theory” – No, you don’t! It’s a very nice way of structuring code, but you can approach it in a much more straightforward way than understanding all the category theory. I think you need to keep a perspective on what’s difficult and what isn’t.
Where things are complicated with monads is when you try and combine different sorts of effects together into a single monad. That’s where things can become complex because in a way, what you are doing there is you are sort of defining a general purpose programming language for each program. I remember a few years ago doing an example of just writing a simple parser using Modula-3, which did use side effects and exceptions in I/O.
Modula-3 gives you this nice way that they are all packaged up together, but if you want to from scratch do something like that in Haskell, you might be tempted to say “How do I put together side effects and operating the outside world and exceptions? Which one fits inside the other?” You should resist that temptation and I suppose the I/O monad does resist that because it really is in a sense an imperative programming language that does put them all together in a single way.
In terms of design space, where the difficulty is is not in monads, but it’s in how you assemble them together. I don’t know what your thoughts are on that, John.
John Hughes: Assembling them together is pretty easy: you just invoke a few monad transformers.
Simon Thompson: But which one goes inside?
John Hughes: Well, you have to do it the right way around.
Simon Thompson: If you are putting three together there are only how many ways to doing it?
John Hughes: Often it doesn’t matter what order you do it in, but in that particular case it does. So you have to think about it. Have you used monads in Erlang?
Simon Thompson: No.
John Hughes: So I said, not as often as in Haskell, of course, because you don’t have to, but it’s just convenient interface to give a data type. If the cap fits, then it’s a good idea to wear it.
Simon Thompson: It’s tricky. If we are talking about views about monads, I always thought that one of the reasons people don’t get their heads around Haskell monads very easily is because representing return and bind is not quite as straightforward as presenting the Kleisli category where you have these generalized functions. These are functions from A to M of B, so they take an input, they do some effects and they return something of type B. It’s clear that bind turns into composition in the Kleisli category and the rules are about as that composition being associative. It’s a much straightforward operation abstractly than the rules for bind.
John Hughes: But you don’t have to show people the monad laws. Nobody approves them anyway.
Simon Thompson: They don’t always hold, but I think abstractly the idea of what you’ve got for these functions, these generalized functions which have some sort of effect and you compose them, I think that’s a nice mental picture. Syntactically, there is other stuff going on, but if you think of that as a thing that’s underlying what’s going on with the do notations, say, I think intuitively that’s clearer. The abstraction AROM of B is a clearer abstraction than M of B in that sense.
John Hughes: For beginners, all you have to do is show them the do-notation, tell them to use the I/O monad and for quite a long time after that, they can just construct monads using monad transforms. They never have to know that bind exists.
Simon Thompson: No, indeed, that’s right.
John Hughes: You don’t need to make it more difficult than it is.
John Hughes: I’ve got two applications in mind and they are both implementations of abstract data types where there is a notion of sequencing that makes sense. One of those applications is in QuickCheck, where the generators for test cases are monadic type. Of course I’ve given them monad interface, so that it’s hidden behind the macro, so you get something like the do-notation. But of course, you want to be able to generate the test data by generating one thing, then generating something else and put them together.
The other application that I have in mind is a library that I’ve been working on lately for defining what I call temporal relations. This is a library for analyzing traces that come out of testing systems. You want to be able to determine whether or not a trace represents a successful test or a failed test. I do that by constructing relations that can contain values at different times. It turns out that these relations are a monad.
If you have a relation that defines one kind of value at a time, at each time and then a function that for each of those values defines another relation, then you can sequence the relations, as it were. What you are essentially doing is forming a union of a lot of index relations. It’s a very convenient way to define many of the more obvious operations or relations. That’s an example - container type, I suppose, which is one classic kind of monad.
Simon Thompson: I just miss the fact that you can’t say whether it’s a maybe monad or maybe type. There isn’t an obvious way of saying that this is the return type of this function. That’s not perhaps as subtle as you’re asking.
John Hughes: In a sense, of course, those monads are used in Erlang programs. There are lots of functions that return either OK in the value or maybe error in the value or whatever. There are lots of functions that take a state as an argument and return a new state as part of the result. It’s just that the monad isn’t factored out and expressed separately and there is no syntactic support for doing so, so that you’d be calling bind explicitly one way or another.
Simon Thompson: Because it’s conventional, you can have one person using “False”, another using “Error”. It’s sometimes nice when things are a little bit formal because everybody uses the same convention. Whereas there can’t be a case you can get people just using very different conventions. It’s essentially the same but different.
John Hughes: Yes. I’m just imagining now. Suppose you did add syntactic support for monads to Erlang. Then, if you go back to your example of the exception and the state monad which you can combine in two different ways, imagine that one programmer combines them one way around and writes some code. Then another programmer combines them the other way around and writes some and then the third person tries to call those two one after the other. Without a type checker I think it would be very hard to figure out what’s going wrong in that case.
Simon Thompson: Definitely.
John Hughes: It may be that monads are something that you really need a type checker to use effectively.
Simon Thompson: There is Dialyzer. Dialyzer will tell you things, but it’s not quite on the status of a type checker, it’s not burnt in. If you look at how Erlang and Haskell differ, you could see what the priorities of the designers were. Concurrency was there right from the start and right in the heart of it. Then Haskell types are there right from the start with no compromises. Whereas you look at concurrency in Haskell and types in Erlang and they are built on top of other things. Therefore there are different constraints on what you can do.
Simon Thompson: I think you use the language that’s appropriate. There are certain situations where Erlang is clearly the right language to use. But I think if I were writing a large scale GUI for an Erlang program, I would think twice before I used Erlang to do that. There is the WX binding, which does make that a lot easier, but I think some languages make those things and provide the libraries and the toolkits to make those sorts of things work, just as Erlang provides OTP. The interesting thing is when you talk about Erlang, it’s not always clear whether you are talking about Erlang or Erlang plus OTP. For the applications where it works very well, it’s Erlang plus OTP that is the real winner.
John Hughes: If you look at real systems, and they almost always seem to involve a combination of languages. And I guess it makes sense to choose a language for each subsystem based on what the needs are. If you’ve got something that is algorithmically simple, but needs to go extremely fast, then perhaps C is the best choice. For myself, though, I very rarely choose anything other than Haskell or Erlang. Life is too short, simply.
One thing that’s important to keep in mind is that although Haskell and Erlang have their differences, they are really very close together. If you are a good Haskell programmer, you are not going to find it difficult to pick up Erlang and start and using it productively and I think vice versa. Despite the differences, there is really one true path.
Simon Thompson: The thing I want to end on is the fact that both in a very good state. If you look at the open source community behind Haskell now, building libraries for this and that and all of these on Cabal [Common Architecture for Building Applications and Libraries], Haskell is in a much better position than it was 10 years ago. It’s much easier to get the standard platform, it’s much easier to download consistent sets of libraries and get them working, there are many more resources out there in terms of books and so on.
It’s in a very good place. Similarly, Erlang is open sourced so it’s possible for patches to go straight into that repository. There are large numbers of people using it commercially. I think they are both on an upward trajectory and I think that’s very exciting.
11. Thank you Simon and John for being here for the interview.
Simon Thompson: Thank you.
John Hughes: Thank you.