The best kittens, technology, and video games blog in the world.

Tuesday, September 19, 2006

My first impressions of Erlang

Father & son by Jeremy_K from flickr (CC-NC-SA)There was so much buzz about Erlang on reddit and elsewhere lately that it all made my actually try it out. The first impressions - contrary to the rumors, it is definitely not The Next Big Thing, and well - it simply sucks.

Now a short list of things that really suck about Erlang - just the stuff you'll encounter in the first few minutes:

  • Lack of basic Unix decency - no man page for erl or erlc (at least in the Ubuntu package), they do not accept --help, one cannot exit by ^D. These are very basic things. If they're broken, people are likely to leave.
  • Interpreter is terribly underpowered - in most languages you can write pretty much any code in an interpreter. In Erlang you cannot even define functions there. Of course there is no online help. At this point I was pretty much willing to leave, but I forced myself to go a bit further.
  • You actually have to compile your code, by hand, before you can use it - if you wrote a hello_world.erl, you have to compile it using erlc, then run erl, and call hello_world:whatever. If you made a mistake, you need to exit the main loop (by halt()., not ^D, to waste more of your time), correct the mistake, recompile, and start again. It is so unbelievably lame that I can't imagine anyone except for C++ programmers being able to put up with it.
  • Don't Repeat Yourself ? Nah - In Erlang you have to repeat yourself a lot. In every file you need to write module name and list of all exported functions. And when you use them you have to prefix them by module name anyway. So even though foo:bar() simply has to be function bar in module foo, you have to explicitly list in on the export list in foo.erl anyway.
  • It is not even marginally object-oriented. I'm not talking about having to hack object-orientation on top of it - it's just that basic object do not know what they are. Lists of numbers cannot tell themselves apart from strings and so on. Writing "Hello, world\n" out will actually output [72,101,108,108,111,44,32,119,111,114,108,100,10].
  • String support is horrible - As I said, strings do not exist as first-class objects, they are emulated by strings. So no Unicode. No regular expressions. No high-level string functions. We're back to C.
  • Not Invented Here Syndrome - Erlang tries it very hard to be different than everything else. Syntax is different (somewhat Prolog-like), Unix conventions are disregarded, standard data types (strings, arrays and hash tables) are not provided, even io:format uses its own format strings instead of following printf. You cannot simply take a look at a few examples and start writing useful programs like with Ruby, Python or even Java. Not following conventions wastes everyone's time.
I didn't even get to distributed computing, as I was hardly able to write an useful single-thread program. On the other hand my first Python program was pretty useful - and all I did was just sitting down, typing some code, and it worked.

Now I don't care much about Erlang, but those of you that do, here's my proposal:
  • Conform to the basic Unix conventions like ^D, man pages, and --help.
  • Throw away the old syntax and add something Python/Ruby-like. This isn't Lisp - weird syntax doesn't give you anything. Writing a decent parser in ANTLR is just one evening, and you don't have to throw away the old syntax, just provide an alternative. When you're at changing syntax, make it possible to access full language from the interpretter and limit the repeating yourself part a bit.
  • Write a decent standard library - real strings with Unicode (not this lists of integers hackery), regular expressions, arrays, hash tables and so on.
  • There is absolutely nothing that forces compilation by hand. Make it automatic by default.
Such changes won't interfere with the fault-safe distributed computing part even the tiniest bit. And if Erlang stays the way it is now, I think it is extremely unlikely to get out of its telecom niche.

25 comments:

Bob Ippolito said...

Most of the things you claim here are only partially true.

* Erlang has LOTS of man pages. If you don't have one for erl, erlc, and everything else then the Ubuntu package didn't install them or didn't install them on a path that man will find them on. I agree that not having ^D is annoying.

* You can define functions at the interpreter, but not named ones. "F = fun () -> 1 end." This isn't normally a problem if you're using Emacs or Erlide because you'll just recompile the module you're working on, which automatically reloads it in the interpreter buffer.

* You do have to compile everything. I agree this is inconvenient, but there's a daemon in one of the test suites that'll automatically do this for you, and either Erlang or Erlide will compile and reload with a keystroke or two. In Python you usually have to restart the whole app when you start changing things, where in Erlang you almost never do.. so it's a tradeoff.

* There is -compile(export_all) -- off the top of my head, might be called something slightly different. There are places where you do have to repeat yourself more than necessary though, mostly to do with record syntax. You can automate a lot of this with code generation (e.g. with smerl).

* It's not object oriented. That's just the kind of language it is. "Types" are usually tagged tuples. "Hello world\n" is almost always printed as "Hello world\n" because lists are scanned for ASCII-ness before they are printed. There is a way to do something like a class instance using a module though (look for "parameterized modules"), but I haven't seen anyone use it.

* String support IS pretty bad, compared to Python, Ruby, Perl. Not so bad compared to C. Not too hard to make it better though.

* The majority of your NIH argument is just FUD. It has all of those data types, and the syntax argument is pretty silly. There are large benefits to having atoms and pattern matching the way they are.

taw said...

I'm not surprised that some of my impressions were incorrect, but I think many people are going to have very similar impressions.

With regards to man pages, it seems that it is simply a bug in Ubuntu package - their own packaging standard require packaging man pages with every executable, but they moved them to separate packages. Still, many people will think that Erlang doesn't come with man pages, and as it doesn't support --help, they will be confused.

Can you write non-trivial functions from the interpretter ? I tried to code a map function, but I was completely unable to.

As for Emacs, nobody told me to try it out (and I don't really like it much) :-) Erlide isn't packaged for Ubuntu, so most people who just want to take a look won't try it. Nobody told me about -compile(export_all) either. I have no idea about smerl, "daemons in test suites" etc. If this is important, it needs to be documented in the first place people look.

As for NIH part (and object-orientation is part of it) - well, life is unfair, but you simply have to do things like everyone else, unless your way is massively objectively better. You can get used to things being different after a few weeks, but most people won't give Erlang that much time.

As nothing forces people to use Erlang (like they are forced to use Javascript to write dynamic websites or to use some mainstream language because their job requires it), about the only way it can get popular is by people trying it for a few evenings, getting immediate productivity boost, and staying. Like they do with with Ruby and Python, or earlier with Perl and PHP etc. This group of people really needs to feel at home in the new language, as they are rarely willing to spend weeks to adapt. If you spent a lot of time with Erlang, you may not see the issue, but for newcomers, it is a huge problem.

And well, it is not that hard to make things more standard, so why not do it ?

Anonymous said...

Man pages? I'm using Windows and I'm using the man pages at www.die.net for reference. If you can't access man pages on *nix then...

You can use lambdas at the prompt.

You can compile from the prompt easily - I don't know if you read the Getting Started guide in the HTML documentation where it is mentioned, but
c(module_name). will do it.

Have a look under stdlib/kernel package for the c module, there's a lot of convenience functions for use within the interpreter.

How is explicitly stating what's an externally available function a bad thing? Do you dislike public/private methods in an OO language too?

And as for not being OO... you might want to look up the definition "functional language" sometime. It's like me complaining because Python doesn't have closures and multiple line lambdas.

Strings are a bit crufty, but you need to do research before saying that there's no string functions, nor regexes... because I've been using the string module and the regexp module in some code I've been writing.

May I suggest RTFMing?

Also the implementation while sucking currently, would allow for strings to become UTF-8 in a further adaptation to the language quite easily.

Speaking of Unicode, there's two functions in the xmlerl application called to_utf8() and from_utf8() - guess what they do?


NIH

The reason the syntax is Prolog like is because... it was implemented in Prolog. I'm not quite sure how the arrays you refer to differ to linked lists, and each individual process has a process wide hash table for usage if you need it.

As for io:format using a different convention to printf() I take it you've never used Lisp's format function then.

*sigh* There are some warts in Erlang, but most of what you've commented on is due to ignorance, and I don't even like Erlang that much.

taw said...

OK, I can use lambdas at the prompt but how can I define a function like map ? It's pretty useless if I can only define trivial functions.

As for oo, well many languages mix oo and functional features. Ruby/Smalltalk are a very good example, but even Perl and Java can do that. Being oo and being functional is not an either-or choice.

As for Common Lisp formats, well they are plain disgusting. You can write something like:
(FORMAT NIL "∼ @(∼ @[∼ R ∼] ∼ ˆ ∼ A. ∼)" 23 "losers").
what prints "Twenty-three losers.". Scheme is a fine language, but Common Lisp ? Yuck.

Bob Ippolito said...

Map = fun (Fun, List) -> [Fun(X) || X <- List] end.

Recursive functions are hard to write at the prompt, so don't do that. Use emacs or erlide. If it doesn't have a package for erlide, then just download it with eclipse. It must have eclipse...

You have a point that Erlang doesn't have a good jumping board for a new user with no training and no desire to ask for help. It hasn't ever been marketed as such (some of Yariv's blog aside).

From what I've seen, the experienced Erlang developers are too busy writing applications to bother with tutorials and books for new users, and they're fine with that (though Joe is currently writing a new book). It's the new users who have put in the learning effort like Yariv that are blogging about Erlang in a way that makes it attractive to potential new users but they aren't bothering to actually write information that would help people become new users.

Cynos said...

"Being functional and being OO is not an either-or choice"

Yeah, it tends to be. You can have a language with functional and OO features, but there's a very important element of FP that you're missing -

Referential Transparency.

No side effects.

If you've used Scheme, it boggles my mind that you missed this. Have you read HTDP or SICP??

taw said...

Well, the ideas of "no side effects", and "referential transparency" have very little to do with functional programming. Functional programming is about building programs by combining subprograms using other subprograms. The combined subprograms are historically called "functions" and combining programs "high-order functions", but they have very little to do with mathematical "functions".

It certainly makes combining easier if there is not too much mutable global state floating around, so it tends to be avoided, but that's how far it goes.

Any language that naturally works with first-class "functions", closures and "high-order functions", is a functional programming language, whether it has "side effects" or not.

Now pretty much all real functional languages - including all Lisps since the 1950s, all *MLs, Smalltalk, Ruby etc. do have mutable state and they do not have anything close to "referential transparency".

And even Haskell doesn't really follow these ideas, it's just faking well. Sure "functions" cannot affect global state, except that they actually can if you use monads. Code inside monads is of course not "referentially transparent" (and pure:impure code ratio in Haskell program isn't much different from a Scheme program, it's just more explicit). Pure/monadic separation enforces different program structure, and that often complicates the program, so beyond certain point you use unsafePerformIO and completely forget that you were supposed to be "referentially transparent". For example I don't think anybody does logging and debugging in Haskell without unsafePerformIO - it would be simply too complex and for no good reason.

And I'm not sure why you're mentioning SICP - it talks a lot about state.

Anonymous said...

> Can you write non-trivial functions from the interpretter ? I tried to code a map function, but I was completely unable to.

You can, but it's fairly tricky.

Basically, you have to use CASE clauses for pattern-matching.

I don't think you can create recursive anonymous functions in erlang though, not implicitely recursive anyway (you could send the function as an argument to itself and use that though)

The thing is that it's useless, Erlang-mode makes it a breeze to work in Erlang, just type your code, C-c C-k, the code is automatically compiled, then you just have to C-x o to switch to the Erlang Shell buffer.

And no I didn't know Emacs, I started learning it because it seemed like the only editor worth using when coding in Erlang or Haskell.

I don't regret it, too.

> As for NIH part (and object-orientation is part of it) - well, life is unfair, but you simply have to do things like everyone else, unless your way is massively objectively better.

Not really. Erlang syntax is not Ruby's or Python's because Erlang is not Python or Ruby. Erlang is a functional language, and functional languages always have alien syntax (just check Haskell or OCaml if you don't believe me Common Lisp too of course), because they have very specific expressive needs.

Erlang was also spawned from trying to get concurrency in Prolog (and the first Erlang interpreter was written in Prolog), which explains the syntax.

As far as strange syntax go, Erlang's isn't that bizarre. Much less so than Haskell's in my opinion, and it's must simpler too. The only quirky stuff I found is the bizarre punctuation (where do I use ","? or ";"? or "." well "." is the simple one)

> String support is horrible - As I said, strings do not exist as first-class objects, they are emulated by strings. So no Unicode. No regular expressions. No high-level string functions.

Strings are implemented as lists of integers. The only bad things about it is that it's inefficient in storage and computational time.

Yet most functional languages implement them that way (Haskell does it too, except that people have recently built an additional extremely efficient ByteString lib).

Erlang does have a regex lib (try `m(regexp).` in your ERL prompt), it does have a high-level `string` module (`m(string).`, try it out), and the fact that strings are merely syntaxical shortcuts to lists means that you can do everything you can on a list (which includes pattern-matching, recursive building and destructuring, and anything in the `lists` module).
Erlang doesn't gracefully handle unicode now (as in, doesn't handle conversions and stuff), but since erlang "characters" are really 32bits integers it can manipulate utf8/16/32 strings. Real unicode support would be cool, and nothing in the implementation forbids it.

> # It is not even marginally object-oriented. I'm not talking about having to hack object-orientation on top of it - it's just that basic object do not know what they are. Lists of numbers cannot tell themselves apart from strings and so on. Writing "Hello, world\n" out will actually output [72,101,108,108,111,44,32,119,111,114,108,100,10].

This is not an object-orientation issue, this is a type-system issue, and the issue that Erlang doesn't allow type aliases or user-defined types.

> As for oo, well many languages mix oo and functional features. Ruby/Smalltalk are a very good example, but even Perl and Java can do that.

They're imperative/OO languages with some functional elements, functional languages are in a wholly different ballpark. And they really do have nothing in common with imperative/OO.

Things like bind-once and side-effectlessness just aren't possible in imperative or OO languages for example, because they rely on side-effects and mutability to work. If you think of functional languages as "the same things, but with more functions and less objects" it's useless to try.

The only languages that are truly functional and yet integrate OO/imperative constructs have even more "bizarre" syntaxes, queue OCaml or Mozart/Oz.

> regular expressions, arrays, hash tables and so on.

Arrays? WTF why would you need arrays? (Erlang has hash tables already, how about looking in the damn STDLIB? and as already mentioned it has regular expressions too)

> Throw away the old syntax and add something Python/Ruby-like.

No. Erlang is not Python or Ruby, it has different needs, different semantics and different functions. If you can't bear it, just don't use it.

And if you can't handle Erlang not having Java's syntax, I suggest you to never come close to any pure functional language, you won't be ab le to bear the pain.

> There is absolutely nothing that forces compilation by hand.

How about code hotswapping?
(and the c/1 shortcut in erl + emacs' C-c C-k make this a non-issue anyway)

taw said...

As for strings implemented as lists of integers - it is fundamentally wrong and cannot possibly work with Unicode. And no, I'm not talking about UTF-8. Basically a single character can be build from a base character and some modifying characters, and representation is not canonical.
Unicode stings [U+0065 U+0301] and [U+00E9] are the same single-character string ("é"). Some discussion on that on reddit. This isn't performance issue, or personal view, or some technical detail - this is fundamental issue. And not only in Unicode, you get the same problem with most non-trivial (CJK and so on) encodings.

As for syntax, being functional is no excuse for weird syntax. Ruby is a functional programming language with Python-like syntax. Nemerle is a functional programming language with Java-like syntax (both also come with oo, and they look very nice).

Haskell syntax is non-standard but is somehow immediately readable. Lisps take some getting used to, but they have really great excuse for it (macros).

On the other hand Objective Caml syntax is a disaster - it is not just weird, it is a major productivity issue even after years of coding in it. Basically they kept adding extensions with new syntax, and now it's much more complex than Perl's and typos lead to really weird bugs). I don't think Erlang syntax is as bad as OCaml, it's just weird. Still - as there are so many functional languages with more standard syntax, so what's the point in making excuses ?

Bob Ippolito said...

As for strings implemented as lists of integers - it is fundamentally wrong and cannot possibly work with Unicode.

Are you on crack? Everything is fundamentally implemented with lists of integers. It's library functions that make things work. Anyway, the majority of languages I've worked with make normalization an optional step when working with unicode.

Also, what's with this "standard syntax" bug up your ass? Erlang's implementation roots go back TWENTY YEARS! Comparing it to a new language like Nemerle, which is no more than three years old, is silly. Languages don't get up one day and change all of their syntax, so it's a worthless topic to bring up.

Andre Behrens said...

Ruby is not a functional language! What makes a language "functional" is not the presence of "functions". Ruby is an object-oriented imperative language. Functional languages are based on lambda calculus. This is not a matter of opinion. It is just The Way It Is. In a functional language, you can't change a variable. This is a good thing. It's part of why erlang is so stable, and why it does concurrency so well. You can create threads for nothing, and not worry, because you know that, within your various functions, nothing outside of the function can break your function. Erlang can run thousands of simultaneous threads, effortlessly. Did you try the tutorial? It's just not an issue. So you spawn your many threads, and they pass "messages" to eachother. When they're done, messages are sent with new data.

This means a few things. First, it matters hardly at all what computer a given computation is on. Every process is in a little bubble. So distributed programming is, I kid you not, nothing.

Erlang is also designed to fail gracefully. When something goes wrong in one thread, it tells another thread "I fucked up" and the other thread cleans up.

The coolest aspect, to me at least, is that you do not have to take the system down to bring in new code. If you have a bug, you simply reload your code. Broken old processes will stay broken. New processes will be fixed.

This is only possible because of the "weird" way erlang is written and designed. Since every process is self-sufficient, you don't have to worry about one swapped module screwing anyone else. It just plain can't happen. Cannot. Can't. As in not possible. It's in the design.

So again, this all happens because of the design. Try to achieve these feats reliably in ruby or python. You can't. Different aims. Different strengths.

Note that I love both ruby and erlang, for entirely different reasons.

But given the move toward distributed programs and multi-core machines, I think erlang has the edge. I think instead of poo-pooing it's strangeness, you should read a book, and really try, because people who understand functional programming are going to have a big leg up in the coming decade.

taw said...

Andre Behrens said:
  In a functional language, you can't change a variable.

Well, let's see - as far as I can tell, *the* functional programming language is Lisp in all its incarnations, and in all of them you can change variables any way you want. Even in very abusive ways. I guess *MLs are also pretty typical examples of functional programming languages, and, well - you can change variables there too.

So you must be refering to something completely different than "functional programming", did you mean "Haskell programming" maybe ? Haskell programming has as much to do with functional programming as C++ with object-oriented programming - they are only remotely related, but they're advertised by some as if they were synonymous.

And well, what is the basis of your claims about Erlang magically solving all the worlds' problems and other languages being completely unable to do anything close to that ? Is it based on real world experience, some theoretical models, or is it simply how you feel plus some anecdotal evidence ?

Brian said...

Lisp is certainly not "the" functional language. You can perhaps call it *a* functional language in that it does provide support for functional programming, but just because it *can* be used for FP doesn't mean everything you write in it is functional. To use your C++ example, C++ is an OO language, but if you write a program in it with no objects involved at all, you are not doing OO programming. If you write a program relying on mutable state or non-idempotent functions in lisp, you are not doing functional programming, regardless of whether it is considered a functional language.

No side effects and referential transparency have *everything* to do with functional programming. Saying they don't is using the term in a widely different way from everyone else in the community. You really need to sit down and read something it before you make any more claims like this.

Anonymous said...

"""Well, the ideas of "no side effects", and "referential transparency" have very little to do with functional programming. Functional programming is about building programs by combining subprograms using other subprograms. The combined subprograms are historically called "functions" and combining programs "high-order functions", but they have very little to do with mathematical "functions".""" --taw

Functional programming is *all about* the lack of side-effects.

Higher-order functions have little to do with combining programs together, they are functions whose domains and ranges can include functions.

Functions in a functional programming language have *everything* to do with mathematical "functions".


... that said, I would like to see the Erlang shell improved. It's unsatisfying that it lacks some powers.

Anonymous said...

There seems to be some confusion between functional programming style, and functional programming languages.

Purely functional languages are ones in which all functions are like mathematical functions, referentially transparent and all that. Such languages are rare, and Haskell is the biggest example. If you think that the IO or monads encapsulating state makes Haskell impure I invite you to look further into how monads actually work. They're counter-intuitive, but they do give complete referential transparency.

Functional programming style is simply a style of programming that is heavily influenced by ideas and common idioms of pure functional programming. Some languages have been called functional because these idioms are widely used, and largely functional programs can be written in non-functional languages, but there is a difference between pure functional languages and imperative languages with first class functions and the like.

Ruby is a killer language and I love it to death. I use higher order functions in Ruby regularly, and the mixture of imperative and functional programming is a powerful combination for many problems. However, Ruby isn't a functional language, and I think it confuses the debate to call it one.

Anonymous said...

i'm just stunned. talk about 10 pounds of cluelessness in a 5 pound bag. do you know anything about languages or programming?

Anonymous said...

The funny thing is how strongly he asserts his cluelessness as gospel.

TV evangelists take notes...

Anonymous said...

The basic point is this:

Erlang's syntax sucks.

There is a reason why IronPython and IronRuby exist - its that, despite different mindsets, even a big company acknowledges that many people use these two languages because of their syntax!

Revisionist said...

«It's like me complaining because Python doesn't have closures and multiple line lambdas.»

Except that python does have closures.
Get your facts straight.

taw said...

Revisionist: Not really. Python's "closures" are severely underpowered compared to the real thing, as you cannot rebind closure's variables.

Gaucho said...

Most of the things you remark here are very superfluous. It's like complaining about Java, because you need to create a file for each class :|
Or complaining about C because strings are a little annoying to use... etc, etc, etc.
You cannot make a choice between programming languages based only on the "user interface".
Criteria for such selection should be deeper, basically, an evaluation of its non functional attributes against the goals of your project (performance, reliability, disponibility, ease of use, portability, support, etc).

taw said...

Gaucho: Of course I can, that's what I'm doing, see? These small things can easily add up to something very significant - as the most obvious example Java sucks compared to Ruby/Perl/Python not because of just a single feature it's missing, it sucks because of over nine thousand things some small and some big that Ruby/Perl/Python got right and it got wrong.

And C strings being "a little annoying to use", well if having your servers owned is considered "a little annoying" then yeah, C strings can be a little annoying ;-)

Anonymous said...

Inside of erl you can compile code with `c(module).` if your module name is module. And bashing a functional language for not being OOP... not how that works...

taw said...

Anonymous: erlang is not functional, it doesn't even have proper high order functions.

Meanwhile most functional languages have real objects.

Erik said...

I've programmed in erlang for a total of five minutes and written higher-order functions.