Hacker News new | ask | show | jobs
by Cyph0n 3825 days ago
I hear Elixir is great, but I understood and still enjoy what you described in Scala, so I don't see what makes it unique. On top of this, it runs on the JVM, so you get to use the entire history of Java libraries whenever you need it.

Your example:

>> (1 to 10).map((x) => x*x).filter(_ < 40)

Of course, Scala is an extremely complex language, but I just take the features I feel are useful and/or relevant to what I'm trying to accomplish.

Another up and coming JVM language with similar capabilities to Scala is Kotlin. If it can take off on Android, I think it will become quite popular.

5 comments

https://medium.com/this-is-not-a-monad-tutorial/interview-wi...

That link mentions some of the tradeoffs that Elixir and Scala would have just due to the VMs they run on.

Thanks for that! The explanation is concise yet easy to understand.
With Elixir, you can use Erlang libraries(or just plain Erlang) right inside of it, very similar to what you describe with Scala!

I've never used Scala personally, and my Java programming was all school-related, so sadly I just don't have the relevant knowledge to explain the differences.

The functional aspects are not really that different. I think the key one is that Erlang has tail call optimization baked in, whereas Scala achieves something close, but not exactly there, by working around the JVM's limitations. Scala's implementation works really well when you have a function calling itself, or two functions mutually calling each other, and that basically supports recursion. But the inability of arbitrary function A -> arbitrary function B -> C -> D, etc, to be used, without risking blowing the stack, does lead to some very simple, easy to understand patterns for control to be unavailable (see Scala actors 'become' below).

From what experience I have (played with Scala, done real dev work in Erlang) -

The lack of inheritance in Erlang/Elixir is a difference. It's one I like (and from what I've seen, the more a person uses Scala the less inheritance they end up using), but others may not.

Actors behave similarly between the platforms, but have a few differences. Whereas in Scala actors (by this I mean Akka) struck me as reactive, in Erlang they struck me as proactive. What I mean is that Scala, at least when I looked at it, it seemed like you create an actor system, and then actors in it, and they do nothing until you send a message. In Erlang, you just create a new process and at any time it can pause to receive a message. That is, the 'actor' is just a thing that does work and has a mailbox, rather than this reactive interconnection of things. Fundamentally there may not be much difference, but I found the abstraction in Erlang easy to initially grok, and playing with Scala led me to some irritation and difficulty in structuring my program. That may have been just due to my own expectations rather than anything innate, but just pointing out, they are a bit different.

Scala actors also have the idea of 'become' when you want to change an actor's behavior (and in general the behavior of an actor feels more rigid to me). In Erlang, an actor is just a process running whatever code. Between that and tail call optimization, the idea of 'become' doesn't exist; if at the end of the function that is executing in the actor's process I want to perform the same action I just recurse; if I want to perform a different action I call a different function. That makes for a very simple mental model to work from; I start an actor and it immediately starts executing (though that execution might just be "wait for a message"), and it runs pretty predictably until either it gets "wait for a message", hits the end of a function (in which case it terminates cleanly), or dies. If you want it to run the same logic more than once you recurse, if you want it to run different logic you call a different function. So the 'actor' part is nothing special, no hand waving or magic, it just is a single process with a mailbox you can check, and I find that delightful to reason about; in Scala actors felt like special ~things~, that I have to configure and then set off.

Performance is a difference; while the JVM is more performant in just sheer number crunching, Erlang has a memory model that better fits actors; there is no stop the world garbage collection. In general, barring something like Azul (which I have no experience with), you'll see a smaller deviation of latencies in Erlang (useful for web servers and things, where every call taking ~5ms is better than having some calls return in 1ms, and others in 120ms).

Scala borrowed a lot of the distribution and fault tolerance mechanisms from Erlang, but I don't like them as much. They feel a bit more bolted on, and the multi-paradigm mixing means a greater degree of rigor is required to ensure you do things properly. That said, Scala seemed to provide a different level of abstractions for supervision strategies, that I didn't really delve into.

The availability of all the JVM libraries is a double edged sword for Scala; while there probably is a JVM library for whatever you want to do, it also probably behaves in ways that won't play nice with your actors, possibly forcing you to deal with multiple concurrency, distribution, and fault tolerance models simultaneously. In Erlang (in which, I might add, I've found libraries for basically everything I wanted to do, that wasn't something extremely esoteric like talk to a piece of hardware that is only used in (X) business domain...of which there was also no Java bindings, only C), unless it's calling out to external code (NIFs and the like), it will at least be informed by the concurrency model you're using (since Erlang it's actors or nothing), though there may be assumptions and expectations and such that the library makes that you will need to understand or modify.This generalizes to the language as a whole, too; in Scala it's very easy to make tradeoffs that will end up hurting you. In fact, it's practically encouraged, as the language is often sold as a way to slowly move into a more functional, safely concurrent world. The problem I have with that is without being forced to move, a lot of devs won't, and mixing paradigms gets you complexity that is usually unnecessary, and oftentimes leads to you not seeing the benefits you'd have gotten had you limited yourself to one.

Another fault tolerance difference (that won't affect you 99% of the time) is that Erlang has better process isolation. An uncaught exception in one process won't affect another one, unless they've been linked together or otherwise been entwined such that they should cause the other to crash. Scala tries to do the same, and largely succeeds, but it doesn't have the same technical underpinnings due to the nature of the JVM.

The profiling/tracing ability of the Erlang VM is better than the JVM (yes, really). The ability to attach a remote shell to poke and prod your running instance is also flat out amazing; I don't recall if Scala had anything similar. I always found Erlang apps to have a fairly small footprint on my box, whereas my experience with the JVM (though mostly using Java) saw them tending to take a fair amount of resources.

I'd stress the fact that 90% of network interacting Java code is blocking and is therefore not suitable for use with Scala's concurrency model. Worse, it will work fine in testing and small workloads, but quickly you run out of threads in your execution context and find yourself with a broken production system.

Erlang differs for two main reasons. First, BEAM has a scheduler that will prevent blocked processes from tying up OS threads. So even if a library only supports synchronous calls, it will still work without interfering with other parts of your application. Second, everything is culturally designed around OTP (the standard library for concurrent applications). Libraries tend to support async modes in ways that jive well with the rest of your codebase. There aren't really competing standards like Akka vs Finagle in Scala. Again, even if the code does not support async it will still work. Async is just an optimization (not just for performance, but the wide swath of runtime inspection tools work better on async designs).

I'm actually in the process of writing a scraper using Akka. It's quite complex at first glance but I'm hoping I'll be able to use the main features without digging too deep.

I appreciate the in depth comparison between the VMs. I guess I need to dive into Erlang eventually.

Your love for Scala should not prevent you from exploring Elixir. You might discover you like it. And it is not either or...
i write both scala and erlang/elixir professionally

i like the idea of scala's type system (the implementation leaves much to be desired but...) and it's definitely nicer writing process heavy tasks in scala with it's wider access to libraries and the performance benefits of types and the jvm but erlang/elixir are vastly, vastly superior for writing services and long running tasks that are mostly i/o bound

concurrency and parallelism are the core of erlang and everything is oriented around that. there are blocking apis in erlang/elixir but they are mostly implemented as wrappers around asynchronous apis. akka is a noble attempt but it's a very poor facsimile of erlang's actor model