Kostis Sagonas on Erlang, Types, Static Analysis and Refactoring

Recorded at:

Bio Kostis Sagonas has been involved in the development of Erlang and led the team that wrote the HiPE native Erlang compiler. Together with students, he has proposed changes and additions to the language and has contributed to its compiler and runtime system. He has designed and written Erlang development tools (dialyzer, typer, tidier, proper,...) and contributed to Erlang open source projects.

Sponsored Content

The Erlang Factory is an event that focuses on Erlang - the computer language that was designed to support distributed, fault-tolerant, soft-realtime applications with requirements for high availability and high concurrency. The main part of the Factory is the conference - a two-day collection of focused subject tracks with an enormous opportunity to meet the best minds in Erlang and network with experts in all its uses and applications.

I work in Academia, I work actually both in Greece and in Sweden, I have been with Uppsala University for many years, leading a high performance Erlang group there, it’s the group that built the native code compiler for Erlang. We started back at the end of the nineties and since I think 2002 the native code compiler for Erlang has been the Erlang distribution, which we are still maintaining, and I also since 2005 have been with the National Technical University of Athens in Greece, also trying to get some very good students there interested in what I am doing and apparently succeeding in doing that sort of thing. So we are here at San Francisco Erlang Factory, it’s actually the first time I am at the San Francisco Erlang Factory, I have been once to London but this is my first time and it’s also good to see such strong interest from American developers and the companies being interested in Erlang these days.

Everything is possible in this world, impossible is nothing, as some commercials says. But actually one has to be very careful in how they design their system and what are their main goals. So in the past there have been some attempts to put a static type system on top of Erlang, I think those attempts have definitely succeeded in bringing out the message to the community, that types are somehow important for programs. But quite frankly they have not been really successful in being adopted by the language. So since 2004 I have also started an approach of adding some type information to the programs but I did it the other way around from most static type systems out there. So most static type systems are designed to prove, to guarantee type safety of programs.

Type safety in Erlang is actually given, it’s guaranteed by the runtime system, it’s guaranteed by dynamic checks. So in Erlang you don’t really get the segmentation fault as you get in languages like C, which are not type safe. So in my opinion there is very little value in going for a type system that makes Erlang statically type safe. On the other hand what is important is to use some of the type information about existing programs either implicitly because you can do some inference and find out some types for your functions, your programs, or being put there by the programmer explicitly in order to find type errors and that’s exactly the approach I’ve been following in the last seven years now. The good thing about this approach is that you can do it slowly, and you can always be enhancing the type, the power of the type inference you employ and this is what we have been doing, and I think we have been very successful in this respect.

So we have succeeded where others have actually not been so successful in convincing the community of the importance of imposing a static type system. So our approach, what it does, is it tries to do aggressive type inference in order to find type errors. This does not make Erlang a statically typed language but it brings most of the benefits of statically typed languages to a dynamic typed language, I think it’s a very interesting, very neat approach.

First of all it’s totally optional that a programmer declares types. But even without declared types we can find a lot of type errors, believe me there is a tool out there called dialyzer that detects discrepancies, it’s a discrepancy analyzer of Erlang programs, that’s what the acronym stands for, and even without any help from the programmer it can detect considerable numbers of discrepancies that might exist in programs.

It can of course find some of the problems that are identified also by static type systems, so for example you call a function that eventually will - that definitely will employ addition in two variables and one of these variables for example is not a number, so you will get a warning that you are calling this function for example with an argument that will eventually be used in an addition but it’s not a number. So you get this type of warning. But, you also get some other warnings that it’s very difficult for statically typed languages to detect, for example very few languages, actually no real language that I know warns you that you are about to call the function 'head' on an empty list.

In Erlang with dialyzer we can actually find very easily these warnings because the system, the underlying type representation does not insist that the empty list and cons are of the same type. So we infer for example for 'head' that it will succeed, that’s the idea of success typing that we have introduced. It will succeed only for, meaning it will return a value, that’s what succeed means, that it will return a value only if its argument is a non empty list. So that’s an example of a type error that we detect and most statically typed languages don’t. On the other hand what we can’t really do with this approach is guarantee the absence of type errors. But in my opinion everything is a matter of definition what is a type error and what is not a type error.

To understand what I am talking about, have you ever thought why division by zero is not a type error? You can easily say "The division function will take any number in the first position, any number but zero in the second position", the problem is that most statically typed languages out there not express easily that there are all the numbers but zero. So they don’t consider that thing a type error.

We have actually extended dialyzer the last two years; I have a student in Greece who has extended the dialyzer tool to detect various kinds of concurrency errors. We started with race conditions and in Erlang there are quite few race conditions, but actually because there are few race conditions the problem is in some sense more serious than in other languages, because programmers are not so used of thinking that there might exist some race conditions so we have extended dialyzer to do a static analysis that detects some kinds of race conditions that might exist in Erlang programs.

We have recently extended it to detect some message passing errors, errors in how message passing is used or to give you an example, suppose you have two processes and one of them is expecting to receive some messages of type Foo but the only types of messages that are being sent there are messages of type Bar and this is the typical kind of message passing error that we can be detecting. We have recently even extended that to detect some kinds of dead locks that might exist due to wrong uses of message passing, or due to confusion on how to use some of the behavior patterns, these are design patterns for concurrency that Erlang supports.

Yes. In Erlang in principle there is no shared memory and actually this is mostly true. There are some built-ins that implicitly use shared memory to efficiently implement some very important functionality for the system so one example of this is the process registry. You can register processes under a particular name so there are two built-ins, one called 'whereis' which takes a name and will return its process identifier if there is a process registered under that name otherwise it will return "Undefined" meaning that there is no such process registered under that name and there is another built in called 'register' which tries to register a process with a certain name.

So many people that come from typically imperative background or they are used to that way of doing things, they might write code that first checks using whereis whether a particular process is registered under that name and if it’s not registered, try to register it. Now that is the wrong way of doing things because if you have two processes trying to do the same thing there, there might be a race condition that both processes might try to register some other process under a particular name. So that’s a typical kind of race condition that might exist. Now this is actually somehow problematic because you are going to get in Erlang an exception thrown if you try to register two different processes or the same process under the same name.

And the exception is actually quite clean and you can catch it and do things, but it’s very difficult typical to restore the state of the machine so you can have some kinds of race conditions even in Erlang programs.

Yes, a very big part of my effort is being spent in creating tools that make the life of programmers easier, especially disciplined programmers, programmers that take static analysis tools seriously and use them, and leverage all the power that static analysis tools can give them, tools that help in testing, automatic testing typically of programs, tools that find certain kinds of errors, by exhaustively testing certain kinds of applications of critical applications that users might have. Another tool that I have been developing the last one and a half year I guess, is a tool that tries to integrate the language of types and specs, the functions specifications that one can have in modern Erlang with automatic testing so it’s partially inspired by QuickCheck but it goes further than that in the sense that it’s fully integrated with the language of types and specs and can use this information to automatically provide some generators for different terms, so one can automatically check that the specifications.

The contracts the programmer writes for functions are indeed not easy or falsifiable by automatic random testing. And one can also write properties for functions that his or her functions are supposed to be satisfying so that then the tool automatically generates test cases for these properties and tries to invalidate them. This is a tool called "PropEr" that was presented here yesterday for the first time, and it’s available on Github for those of you who are interested in this sort of thing.

I can explain you very much about it, but I think you will be more convinced if you actually see either some of the slides in the talk which contains some actual cases of tidying code and also some of the demos that exist on the tidier site, that you can see.

I think it’s very impressive I am really proud of that and I am really surprised that they don’t see the same idea in other languages. So the idea is really simple: people, programmers as they get to know a language better and get more experience in the language, they typically develop some better ways of writing programs, so what you wrote, you typically may have written some code the first week that you learn the language and you might look on that code three years down the road and you might be thinking "Why did I write this in such a complicated fashion?" Even if you are an experienced programmer you might for various reasons be in a hurry and you might be using some very convoluted way of writing some function in your program, so good programmers or programmers that actually have the time to do that sort of thing, they go and refactor the code, they make it simpler, they use a newer construct that might exist in the language, they try to shrink it, they try to make it prettier, they try to do all sorts of refactoring of the code.

So the idea I had was why can’t we automate this in some way? Why can’t we try to see whether there are some patterns in things that experienced programmers do? And try to employ static analysis to recognize cases where these patterns exist, second where there is a semantically equivalent way of writing the same thing, in a better, in a smaller, in a more efficient way and then employ static analysis to guarantee that we can always change one convoluted way of writing some functionality into a simpler, a smaller one. And it’s really amazing if you see some of the slides that are on the web, you can see that the tool can automatically take eight lines of code and transform them into one line.

Yes, the slides contain the interesting cases that we found, there are some other ones that are pretty boring, but they are totally automatic, and it’s actual code that has been out there, actual code from big companies that I will probably not mention at this point, but you can see where all this code came from. Tidier I think is a very cool idea, and I would like to see such tools for other languages too. I am a bit surprised that they don’t really exist.

I think in all languages you need a good tool set and there are some languages where a good tool set is just a fancy IDE with pretty colors. There are some other languages where you can have an editor with your favorite colors and with your favorite name of editor, I am not going to go into the language, to the editor war here, but there are other tools that one can employ systematically, have them as part of their makefiles, that I am always going to be running my unit tests, and make sure that what was working yesterday still works today after the changes I’ve made to my code, I’m going to be running my static analyzer, to be finding issues in my code that definitely need to be fixed, I did not actually mention that dialyzer is different than many of other static analysis tools out there, that whatever it reports is not noise. There are very good reasons why dialyzer gives certain warnings, and we tried to be very conservative in not having any noise whatsoever for the programmer.

Actually it does make sense but I think you can get most of the benefits of static typing with the approach I tried to sketch in my previous answers to this interview. Now if you really want to go that way one has to be a bit careful, because if you try to impose a static type on top of an existing language that wasn’t designed with this in mind, chances are that there will be some things that you will have to rewrite and some of these rewrites are going to be simple, some other ones you really have to scratch your head really hard in order to actually implement them in a way that satisfies whatever desires the type system you have put on top of the language might have. So I am wondering about the convenience of this approach. On the other hand there are good reasons to do that sort of thing for some kinds of applications. But as I said you can go a long way with just the approach dialyzer is taking.

There are many interesting languages out there and perhaps this is more a problem than a solution, because at the end of the day I still have only twenty-four hours to work every day and to learn new languages. There are languages that are interesting these days, Scala is for example one very interesting language that I can mention, partially because it’s inspired by some things that Erlang has, it tries to integrate the actor model into the Java type of languages. Another language that has been very interesting for me, although it’s not so in fashion anymore, is Prolog. I started from a logic programming background occasionally I go back and try to see some of the things that exist in a language like Prolog to how they can be adapted in another language.

Yes, and actually that is one of the reasons why I got interested in Erlang because it was closer to what was dear to my heart at that time, and also it was made in Sweden where I ended up and it was a natural way of me getting involved in the language.

It’s a hard question to answer I think it’s worth trying. I very much like all these attempts that try to combine nice ideas from different languages. As a researcher, I think I find this very interesting, now at the end of the day I think you need to step back a bit and see whether these things really fit together. For some applications if you have a very nice controlled library that gives you the power of the actor model, you can go a long way but for a general language perhaps it’s going to be too difficult and sometimes there is a cost of doing that sort of thing, typically for example in Java virtual machines, there is big cost in having shared memory by default. And this cost comes both in terms of size of memory and in terms of garbage collection overhead that you have to pay. In Erlang the default is the other way around, typically you have very lightweight processes, that’s the default language is not an afterthought.

The memory of it can automatically be reclaimed. It’s a constant time operation as opposed to a linear time operation that might be in other languages. And the good thing about it is that it does not disturb any other processes that might be running at that time, this can happen totally independently of the other processes. There is very little memory that is shared like for example the process registry that I described before or perhaps some of the area used for big data that might exist.

Multicore is very interesting these days because if you go to any shop and you try to buy a machine these days even your grandmother will buy at least a two core machine. And typically if you want to buy a machine these days for your Desktop you are going to buy a machine with more cores than just two. So these cores somehow have to be used, so people try to go back in ideas that were explored in the eighties to do parallelization of programs. Now, I think they are missing there an opportunity; there is a big difference between parallelizing your code and designing from the start to be concurrent. And the default in Erlang is to design your applications from the start to be concurrent. Because concurrency is not just a mechanism to get better performance, it’s a mechanism of organizing your code, among entities that can work independently and can do specific tasks.

To give an example, long ago, before I had the chance of having multicores in everyday use, we decided to make the native code compiler for Erlang concurrent, meaning that after we do the semantic analysis of the code we are going to be compiling each of the functions independently. This was actually a three-line change in Erlang. And since 2005 we have that. And with every new machine that comes out there we get a speed up, naturally, just because we did a three-line change back in 2005. I think this is a bit impressive and I am wondering how many other compilers out there are doing that and it’s very natural to be having in Erlang for example a single process to be compiling a single function that’s exactly what compilers are doing.

I think it will be. First of all I think it’s highly unlikely that we have to go back to one core, but even if we did, as I said it’s a way of organizing programs. So concurrency is something that is natural, it happens around us naturally. So why shouldn’t it happen in the programs? I know that what I am talking about now it’s not a very scientific argument, but it’s a better way of organizing your thoughts: you split a program into smaller parts, and then you try to do these smaller parts concurrently, with as little interaction between these parts as possible. If you happen to have the luxury of having more than one core you get automatic parallelization, otherwise you just have very nicely organized program into concurrent parts.

If we end up in your example world, they might execute serially, sequentially, but I doubt we end up in this world, we will go to, at least for the foreseeable future, we are going to more and more cores, and if you have designed your programs like that, you are going to get automatic speed ups with minimal effort.

Is your profile up-to-date? Please take a moment to review and update.

Email Address

Note: If updating/changing your email, a validation request will be sent

Company name:

Keep current company name

Update Company name to:

Company role:

Keep current company role

Update company role to:

Company size:

Keep current company Size

Update company size to:

Country/Zone:

Keep current country/zone

Update country/zone to:

State/Province/Region:

Keep current state/province/region

Update state/province/region to:

Subscribe to our newsletter?

Subscribe to our industry email notices?

You will be sent an email to validate the new email address. This pop-up will close itself in a few moments.

We notice you're using an ad blocker

We understand why you use ad blockers. However to keep InfoQ free we need your support. InfoQ will not provide your data to third parties without individual opt-in consent. We only work with advertisers relevant to our readers. Please consider whitelisting us.