Yes. I am Jesper and I am basically one of the Danish programming language geeks that likes to play with programming languages and I play with all kinds of programming languages and try to use them for different kinds of things and look into them. I mostly do functional programming, but I also do certain programming or look into programming languages which are imperative. So I try to come all the way around.
The original idea was that I wanted to basically learn the language. In order to learn a language my basic idea is you have to try to implement something in the language, you have to use it for something, you have to use it in a way that is more than just a toy example. You have to use it for a real problem in order to really understand what the language does. And that basically was the start that started me off on Erlang and BitTorrent clients are heavily concurrent and massively parallel so that part of it meant that I thought that Erlang would be a good fit for implementing BitTorrent clients.
Then the game was to try to see first of all as a personal challenge "Could I do it?", but then also the challenge that I wanted to learn this language, I wanted to understand what the language did. So that was the reason why I started with writing the BitTorrent client in Erlang. And then later on the same thing happened with Haskell and then what happens is that since you already implemented the BitTorrent protocol, you so to speak know a lot about it, you know how it works, you know all the nasty parts of it, you know all the parts which are hard to handle. That is still not trivial to implement, you still have to do some hard parts, but since you know in advance what is probably going to be trouble, it means that you can basically learn the new language quicker.
Because what you then do is you can focus on what the new language has of features and you can focus on those features alone because you freeze the task which is now to implement the BitTorrent protocol. Again the goal was to learn the language, the goal was to understand Haskell to a new level compared to the level I understood it earlier.
4. What is so special about Erlang in the first place?
What is so special about it? To me there are two things that make Erlang special: the first one is that it has this reactive idea of error handling that if an error occurs in the language, then you have a contingoncy plan that will basically remove or try to solve the error in some way and try to make sure that your program is still continuing to run rather than just die and stop. So we have this continuation guarantee on the system that it will do everything it can to try to continue running. And that idea, I think, is quite unique the way you write that thing out, the way you react to errors in Erlang. This is very unique to Erlang and I really like that thing about it. And I think that is one of the things I find interesting about it.
The other one is the internal model where you have a large number of small processes, but each process is completely isolated to all other processes. So normally when you have programming languages the thing is that you might have many processes, but they are sharing the same memory space, and that is not the case in Erlang when you are looking from the VM perspective. Of course, down bellow you have a shared memory space, the kernel sees it as a shared memory space, but in the Erlang mental model you are basically looking at each process as an isolated thing. Now that has a cost and the cost is that if you move something from once process to another you have to copy things over.
But I think that since Erlang is the only language that basically follows this model that I know of, so to speak, more popular, there might be a toy language out there that actually does it, but many of the normal languages are using the shared memory idea rather than this each process has its own garbage collector and it’s completely isolated. So that is the second thing I think it’s unique to Erlang.
Haskell has a shared memory space and when you do things, when you can fork processes, but memory space is shared. Luckily what is happening here is that you have this persistence immutability in the language and the persistence immutability, the thing that things are immutable, does that you can get fairly long way even though you do not have complete process isolation, the point that if I send a data structure to another process, it gets essentially the copy. Because if I then lay down all of the data structure I just sent to him, that will be a new version of the data structure and the persistence saves me because he still has a valid original static view of the old version.
So basically you can see it as you have versioning of your data and that solves the problem of not having this complete isolation. Immutability and the persistence in the functional language of Haskell solves a lot of these problems in Haskell.
6. What about the error handling in Haskell?
That is an interesting part because Haskell does not have this reactive idea of error handling. What Haskell does is that, if an error occurs you have an exception in the usual way that you can handle, that is the way it’s done, if there is an error. But what I think that is the main idea in Haskell is that you have a very strong type system in Haskell. So Haskell is, sort of speak, proactive about error handling in the sense that the type system’s expressiveness you are supposed to use that and exploit it such that your program has fewer errors and fewer error states because they can never occur in the program in the first place, basically due to the type system. The type system will basically not allow that you write a program with an error in.
And here is an important point which is that you can view type systems in two ways: you can look at them as something you do not like and then they will usually will not be that effective because what you do is you are not using, you are not exploiting that you have a type system. Rather what you do is that you basically try to cope with having the type system and it irritates you. If you take the other view, you look at the type system as a way by which you can specify your program. Hence, you can exploit the type system in way such that if there is an error in your program somewhere, then there is a great chance that when you write your code and make the error the type system will protect you.
The thing is that you are writing your types in a way such that if the error occurs, there is a great chance that the type checker will come out to say: "Hey, that is not good! You made a mistake here." And it can even place its finger down on the line where the potential bug is. So I think that is the difference between the two that both Haskell and Erlang are obsessed about correctness or robustness of programs, but Haskell has more taken the pro active approach and Erlang has more taken the reactive approach to gaining the robustness of the program.
So in the Haskell client, if there is an exception, I do something which looks a bit like the error handling in Erlang, so I implement a part of the Erlang error handling, but it’s not a complete error handling of Erlang I have. I only have a part of it, so if it dies it actually dies. It’s not like it survives, it’s not like it restarts part of the system if certain parts of the system die as it does in Erlang.
7. In Erlang you use actors, Haskell doesn’t have actors. So what did you do?
Not per se. The thing is that the actor model is an old model, it’s from the 70s and it’s a very specific model and Erlang is very actor like in the sense that it in some ways implements the actor model and then when I started writing the Haskell program I decided that, since I already had a process model built on the Erlang style actors, it was simpler just to take that model and then try to retrofit that into Haskell. So what I did was that (it took a bit of time) I started with that concurrency library in Haskell and CML and I used that for some time and what I did was I reimplemented actors on top of CML. Then after some time I realized that CML was a bad fit; CML was not the thing I needed and there was some abstraction leaks inside CML I didn’t like.
So instead I chose to change the CML part of it to software transactional memory or the STM model which Haskell also has. So what it’s done is what I have this STM model at the lowest level of the system and that model implemented something which is actor-like in Haskell and then the client is build in this actor-like world; that is basically how it’s built.
8. Was it easy to implement on top of the STM?
Yes, I think it was quite easy. There is a difference though: in Erlang what you are doing is you are sending messages to processes. A process is the identifier to which you are sending a message, whereas in the Haskell client you are sending messages through channels, so it’s a channel by which you are sending a message. And I think it has to do to the idea that Haskell is statically typed. It’s definitely much easier to have a channel that has a given type rather than just have a process because then the question of "What is the incoming type to that process?" is going to be a hard question to answer because it has to be some of the other types and those types might be very complex and it turns out that type-wise is perhaps not the easiest problem to solve.
Whereas if you had channels to the language it’s almost trivial how the types in the system would look. So if you have a statically-type language like Haskell the channel approach is probably going to be the simplest one to gain. So when I wrote the BitTorrent client in Haskell I decided to use channels rather than build that aspect of the model. Then, if you have channels there is a channel primitive inside the STM so you are basically done at that point. The only thing you need in addition is you need to be able to receive on multiple channels, but STM also have provisions that allow you to do that. So basically you are almost set from the beginning.
I think the only thing I added is some that handles the processes and handle some supervisor structure that tries to restart part of the thing or handle that appear, suddenly disconnects stuff like that. Everything there amounts to something like 40-50 lines of code, that is enough to take most of the Erlang model and re-implement it in Haskell.
Yes it did. What happened was that I had the Erlang implementation and then when I reimplemented the thing in Haskell, I realized suddenly that because of this channel thing where suddenly I had channels, I had to redo certain parts. But what occurred to me when I wrote the Haskell version was that I got a bit of knowledge of the structure of the individual processes. So the thing I learned was that certain processes are local to appear; it’s only in the context of a single pear that that process a meaning and certain processes are Torrent local in the sense that they only give meaning to a complete Torrent.
There might be a set of peers that communicate with this process locally on the Torrent and then you have global processes which are processes which are communicating with the multiple Torrents. For instance imagine that you wanted to add a bandwidth limitation device to the system, so you limit the amount of upstream bandwidth that you use. That would be a global thing because you want to globally, for the whole client, limit the bandwidth. Then you could also for each Torrent have an individual limit there that limited the performance for each individual Torrent, but that’s a Torrent local thing.
And then if you go further down you could imagine that you have a peer limiter that limits a specific peer to a given maximal upstream bandwidth speed. So that was basically the idea that I got that there is this location thing that you can be global, torrent local or peer local in the system. And the Erlang implementation did not even have that thing in it. It was basically that everything was interconnected. So what I did was that I redid a part of the Erlang process model such that it fitted this idea that I began thinking about:"Oh, this has Torrent local", meaning this process has to go there. It must not be a global process that goes there because then it has to contain state for multiple Torrents at the same time.
So this knowledge of where the location of a given process should be was fit back into the Erlang program because it didn’t have that thing. There is no doubt that doing it in Haskell which was basically the second iteration influenced redoing certain parts in Erlang which is basically the third iteration of this. And you could easily imagine doing a fourth iteration where you make your model even more known to you and your mind.
I like both of them from two different viewpoints. There are definitely advantages to dynamic typing and there are definitely advantages to static typing and vice versa. There are also disadvantages to both models. The point is that I tend to have a bias for static typing. I tend to like static typing a bit more than I like dynamic typing. But then again when writing in Erlang I don’t think it’s that much of a problem because it’s recent in the sense of a couple of years, but the idea is that now you have some specification contracts you can now write down in your code.
There is a tool named the Dialyzer up which analyses the code and when it does that it will find many of the same bugs that a type system would catch. It won’t find all of them but it will find many of them. On the other hand, having the dynamic type system means that you don’t have to have a specific idea of the program structure from the beginning. When I am writing in a statically type language what I usually do is I write down a bit of the type specification.
Then, if I cannot imagine how it would look in the code I will begin programming it and the usually what happens is that will go along programming and then suddenly realize that my types have to change again. So it’s this back-and-forth thing where you are fixing the types going back rewriting code, going back doing types, going back doing writing code. That is what happens when the language is statically typed. But when it’s dynamically typed, it’ more like you go in and you just write the thing and then it’s like clay, where you form the clay slowly and in the beginning there is no clear structure of what you have.
It’s a bit unstructured what you have and then you iterate and you iterate and every time you iterate you get closer and closer to more and more structure on the program. What I find interesting is that in many cases you actually end up with the thing where the two things sort of get closer and closer to each others and almost connect because the iteration part of dynamic typing and this idea of forming clay will move you toward this target point. Then this jumping back and forth between understating more of the program than those, understanding more of the types which, in turn, makes you understand more the program which makes you and so on, it also moves you.
So I think there is a common middle ground that you are slowly near when you write in one methodology or in the other methodology and I think that is quite interesting.
11. You are talking about Haskell and Erlang. What other languages do you feel interesting out there?
I think that the ML family of languages is very interesting. The history is that there was an old language ML and then it split basically in two branches: there is the standard ML branch which is used mostly academically and then there is the OCAML branch which has also seen heavy use in academic work but it’s also used outside academics in places. The basic idea is the standard ML the academic band language whereas OCAML is perhaps a bit more practical band language so it tends to be used more for practical things. But those languages, especially the ML family of languages I find to be very interesting and they are interesting because they are statically typed as Haskell is, but they are not lazy.
They are strict and that makes them a different kind of language and writing in it is different. Also they are not pure, so they actually add all the nasty parts of mutation and imperative code to themselves. So you can do imperative code in them without having to resort to things like monads. That is interesting because it’s a completely different way. When your program OCAML or if you want fast OCAML programs, you have to use a completely different approach than if you want fast Haskell programs. Even though the languages are similar in many ways they are also very different because of the evaluation strategy differs. So that is a language I find is interesting.
I also find the Google Go language to be very interesting because it’s a language that’s sort of the middle ground between many other languages while adding at the same time a concurrency model on top of it. My original interest in it was because it was an imperative language with a message passing concurrency model which were modern which is special. We don’t need have any other language that fills that slot, so to speak. Therefore I think that Go is an interesting language that you have to look at. Then of course I am also in very much interested in Lisp languages. Currently it seems like a language like Clojure, so to speak, taking over in a certain kind of way from the older Lisp because it has this ability to work on the JVM.
So it has a way to sort of leverage the clever ideas of the JVM and build on top of it. Now that also means that certain things are very hard to do because the JVM is not very well suited for writing functional, but I think they are doing a really good job. I don’t know much about it, but I find that it is one of those places I want to look into, because it seems there are some really cool things going on over there and I want to know and understand their thoughts and what is going on in their minds over there and understanding what they are trying to do with the language because it seems to me that they are taking a different approach to many things. I think every time someone comes with a new approach to something you have to look into it because it might be interesting.
12. Maybe the next step is to implement the BitTorrent with Clojure and Go.
Actually when Go came out, I thought about implementing a BitTorrent client in Go. Ultimately they ended up being in Haskell, but it was about the same time the project started. But originally the idea was actually to implement it in Go. Clojure and BitTorrent - I have not looked into that idea yet. I don’t think I would try. So now I met the point where I am probably fed up with implementing BitTorrent clients to a certain kind of sense. So I would probably try to do something completely different, should I implement something in Clojure, just to get a new thing to implement. So that is probably my answer to that question.