
The Unix Philosophy and Elixir as an Alternative to Go - aaron-lebo
http://lebo.io/2015/06/22/the-unix-philosophy-and-elixir-as-an-alternative-to-go.html
======
vezzy-fnord
Erlang (and by extension Elixir) are good languages for web development, I
agree. Frameworks like Nitrogen and N2O even let you do frontend work directly
from base Erlang constructs like records and functions to represent templates
and JS callbacks, respectively.

However, they will not replace Go for a rather simple reason. Erlang is an
application runtime first and a language second. It's not meant to play with
the native OS all that well, instead relying on its own scheduling, semantics,
process constructs and the OTP framework. It exports a minimum set of OS libs
for things like file system interaction, but you wouldn't write a Unix
subsystem daemon in it. Though, you certainly _can_ \- if you want to hack
with NIFs, port drivers and C nodes. But then you put the global VM state at
risk if some native caller blows up.

For this reason, Go will remain a dominant choice for infrastructure and
systems programmers. It definitely _is_ replacing C/C++ for lots of things
above kernel space, contrary to your statement.

I'd honestly dispute the characterization that Elixir is a small language.
Erlang is easier to fit into your head, not least of which is because of how
essential pattern matching on tuples and lists is. It's a consistent and
homoiconic design, more warty constructs like records aside.

Finally, Erlang/Elixir aren't Unix-y at all. Don't get me wrong, BEAM is a
wonderful piece of engineering. But it's very well known for being opinionated
and not interacting with the host environment well. This reflects its embedded
heritage. For example, the Erlang VM has its own global signal handlers, which
means processes themselves can't handle signals locally unless they talk to an
external port driver.

~~~
pcwalton
Golang implements a custom scheduler in userspace too, and this is why its FFI
is not as fast or as well integrated with the OS as the calling conventions of
C or C++ are. Heck, Golang's FFI is even less straightforward than that of
Java, since the JVM is typically 1:1, not M:N.

Once you've gone M:N you're effectively in your own little world. There's a
reason Golang's runtime is compiled with the Golang toolchan, and why it
invokes syscalls directly as opposed to going through libc.

~~~
kjksf
I did some cgo (Go's FFI) work. Go's FFI is vastly superior to JNI (which is
in a league of its own when it comes to awfulness).

No FFI is really great. Go's isn't as good as C# or Luajit or Swift but better
than Java or Python or Ruby or Lua FFI (where you have to write a wrapper for
each function you want to expose to the language).

C and C++ _are_ OS calling conventions so I'm not sure what that was supposed
to mean.

It's true that go is M:N and that it does have bearing on bridging _some_ C
code (in rare cases where C code only works when called on main thread).

However, gccgo has Go runtime written in C and compiled with gcc, so go's
runtime isn't tied to one toolchain.

I don't know why Go designers chose to (mostly) avoid libc but it certainly is
great for portability and ease of cross-compilation. If you take dependency on
libc (which is different between Linux/Mac/Windows) you pretty much throw the
ability of cross-compile and given Go's static compilation model would require
bundling a C compiler (unlike runtime-based languages like Java, Python or
Ruby, where only runtime has to be compiled on the proper OS, so that
complexity is contained for the programs written in the language).

I don't see why do you ding Go in particular for being "it's own little
world". Compared to C - of course. Compared to Java or Python or Elixir? Much
less than them.

~~~
pjmlp
On Java's case it was made awful on purpose, to discourage developers from
writing native extensions.

The new FFI is on its way, but it might arrive only on Java 10.

~~~
jerven
Its of topic but I think it was a great idea to have terrible C interop. It
really forced people to write java all the way. This meant the JVMs could
really evolve unlike the Python and Ruby ones.

I am not sure the new java FFI is such a great idea in the long run. I would
rather that they spent more time focusing on object layout and GPU compute ;)

~~~
beagle3
> It really forced people to write java all the way. This meant the JVMs could
> really evolve unlike the Python and Ruby ones.

Java's FFI is horrible; Python and Ruby are mediocre. LuaJIT2's is fantastic.
Not so surprisingly, Python ate Java's lunch in places like scientific
computing, where it is much more beneficial to build on existing work.

Python is hard to dethrone from that spot right now because of momentum,
mostly - but if the competition was started again, I'm sure LuaJIT2 would take
the crown (Torch7 is based on it, but that's the only one I know).

I think my bottom line is: If you want your VM environment to be self
sufficient, have horrible FFI like Java. If you want your VM environment to
thrive with existing codebases, you have to have at least a mediocre one like
Pythons. But you can have the best of all worlds like LuaJIT[2] - and that's
who Oracle should be copying.

~~~
pjmlp
I think Python will loose momentum as soon as Julia gets more adoption,
likewise with languages like Go and ML derivatives. Unless PyPy gets more
widespread that is,

Upcoming Java's FFI is based on JNR, an evolution of JNA, used by JRuby for
native FFI.

Nevertheless everyone seems to have forgotten about CNI, implemented on GCJ,
which mapped C++ classes directly to Java ones.

------
aikah
> That being said, have you tried writing a web app in Go? You can do it, but
> it isn't exactly entertaining. All those nice form-handling libraries you
> are used to in Python and Ruby? Yeah, they aren't nearly as good. You can
> try writing some validation functions for different form inputs, but you'll
> probably run into limitations with the type system and find there are
> certain things you cannot express in the same way you could with the
> languages you came from. Database handling gets more verbose, models get
> very ugly with tags for JSON, databases, and whatever else. It isn't an
> ideal situation. I'm ready to embrace simplicity, but writing web apps is
> already pretty menial work, Go only exacerbates that with so many simple
> tasks.

This.

And don't even try to work around these limitations and share your work for
free, or you're going to get trashed by the Go community( i.e. the Martini
fiasco )

But think of go more as a blueprint. Go has great ideas(CSP,go routines,...)

Hopefully it can inspire a new wave of languages that don't rely on a VM, that
are statically typed and safe without a fucked up syntax.

The language that succeeds in striking the right balance between minimalism
and features with an excellent support for concurrency will be the next big
thing.

~~~
bsder
> ( i.e. the Martini fiasco )

Reference please for those of us who are only casual Go users?

~~~
Gigablah
The author of Martini released his framework, some people in the community
said it wasn't idiomatic Go, he agreed and eventually released another one
called Negroni.

I don't know if he was "trashed", or whether there was a "fiasco".

~~~
enneff
Jeremy Saenz certainly remains a well respected member of the Go community.

Martini is very 'magical' and makes it hard to see exactly what's going on,
making it quite unusual as a Go framework, so naturally it drew some
criticisms. After some experience with the library, Jeremy realized that he
actually needed a lot less to do what he wanted, so he wrote Negroni.

------
odiroot
> Somewhere, however, that has been extrapolated to the idea that Go is a good
> language for writing web apps. I don't believe that's the case.

I have always had this feeling and thought why isn't more people speaking up
about that.

For me it's a good improvement from C++ (and maybe C as well), somehow well-
suited for systems programming.

I never understood how people can claim it to be natural "upgrade" from Python
or Ruby. I have a feeling the opposite is actually the case. This of course is
all in the web-development context, not in general one.

------
prasoon2211
I am utterly baffled by people who use the phrase "The Unix Philosophy" for
justifying a piece of software but don't understand that Unix philosophy is in
the context of software that can be _composed_ together. That is why the other
tenet of the Unix philosophy is to use text streams for all I/O.

------
jordan0day
I think Elixir and Go solve different problems -- but for the problem space
the author mentions here, web services, Elixir is clearly superior.

------
oldpond
Thanks for that interesting viewpoint. I have also done the same survey, and I
agree. I did a ChicagoBoss PoC last year, and Elixir and Phoenix are my goto
for the next one.

It should also be noted that both are young languages and they are evolving
rapidly. This makes them even more exciting to me. Also, I think Go's sweet
spot may be a new approach to client/server i.e. applications that work across
the network. This is quite different than the browser/app server model. Elixir
has similar plumbing that fit this model as well, but what really excites me
about Elixir and Phoenix is the ability to build a restful MVC application
that can take advantage of Erlang/OTP's scalability.

~~~
aikah
> It should also be noted that both are young languages and they are evolving
> rapidly.

Go will basically get dynamic linking and stop evolving. Its type system is
way too simple to support any new significant feature. And go designers have
said many time that the language is "done".

~~~
Dobbs
My understanding is not that they have said that the language is "done", but
that the 1.0 branch is stable and won't get any new major language features.
At somepoint they will start rolling out 2.0 and it will have new features,
maybe even generics. But why should they worry about that the language is
plenty good as it stands for developing and their is plenty of work to do on
cleaning up build systems, runtime, gc, etc.

~~~
aikah
> "The language is done and that's a good thing," Buberel says.

[http://finance.yahoo.com/news/google-thinks-knock-one-
oracle...](http://finance.yahoo.com/news/google-thinks-knock-one-
oracles-233951894.html)

Pike said exactly the same thing(video is on youtube), another Go guy said
exactly the same thing in a podcast ...

So yes, Go language is DONE, period.

> But why should they worry about ...

They don't, however people adopting Go and wanting generics shouldn't be
tricked into thinking that Go developers are even considering generics ,
because they are not. If they invest in Go, they should know that they aren't
going to get generics at all. As for what the Go team thinks, it doesn't
really matter they are not betting their own startup on Go.

> they will start rolling out 2.0 and it will have new features

where did you get that idea? it is impossible to add significant new features
without breaking backward compatibility, do you really think Go designers are
ready to go that way? of course not. But i'm curious what led you to think
that ?by all means, give me official sources like I just gave you.

~~~
danieldk
_So yes, Go language is DONE, period._

 _They don 't, however people adopting Go and wanting generics shouldn't be
tricked into thinking that Go developers are even considering generics ,
because they are not._

Wrong:

 _Generics may well be added at some point. [...] This remains an open issue._

[https://golang.org/doc/faq#generics](https://golang.org/doc/faq#generics)

~~~
aikah
If you understand one or two things about type theory then you know that
generics cannot be added to GO. Go designers know it. They cannot add generics
without breaking the language and they will not break the language.

When you have at least 3 core designers of that language saying on record that
the language won't have new features, then you know what is written in that
Faq ain't going to happen.

So let's not mislead people interested in Go into thinking that it will get
generics, it will never have generics.

~~~
jbooth
I think you need to justify that statement, what about type theory says that
Go can't have generics?

As a follow-up, would the same logic have proved that Java 1.4 couldn't add
generics?

------
jtwebman
I have been using Elixir for a while now! I'll never go back to Go or Node.js
by choice.

~~~
ams6110
Why Elixir over native Erlang? Nobody including the original piece seems to
address this. Is it just a subjectively nicer syntax?

~~~
biokoda
The biggest obstacle to erlangs adoption is that it does not have a low
barrier to entry. The syntax is strange, the way of structuring and building
solutions is strange. It is incredibly powerful, but it requires a different
way of thinking.

Elixir makes sense because it is a lower barrier to entry. This is a very
important factor. It is one of the main reasons why node.js was so successful
in reaching a large audience. It is why Go is more popular than Erlang. They
are often used in the same domain and Erlang is arguably more powerful. People
still choose Go however.

~~~
pjmlp
Strange for those that never did Prolog.

I was really into Prolog back in the university days, to the point of being
into logic programming competition between universities.

Got to understand Erlang at the first try, when reading about the language or
watching web talks.

It pretty much depends from where one is coming from.

~~~
biokoda
The vast majority of programmers never used or cared about Prolog.

~~~
porker
They have missed out.

In terms of elegance, I've never written anything that surpasses Prolog _.

_ For a defined subset of problems.

------
applecore
_> However, as we more and more turn servers into REST APIs that are supposed
to deliver JSON to numerous clients, for many it is time to find an
alternative._

Perhaps the alternative isn't more REST APIs that deliver JSON; given its
inherent problems, it's more likely we'll be using a challenger to that entire
model, like GraphQL.

[https://facebook.github.io/react/blog/2015/05/01/graphql-
int...](https://facebook.github.io/react/blog/2015/05/01/graphql-
introduction.html)

~~~
jtwebman
I willing to bet once the spec is out there will be a Elixir implementation of
GraphQL. I am also willing to bet Elixir would still be faster then Node.js,
Ruby, or Python for that.

------
fiatjaf
Code samples side-by-side comparison:
[http://rosetta.alhur.es/compare/go/elixir/#](http://rosetta.alhur.es/compare/go/elixir/#)

------
vinceguidry
Just keep using Ruby or, if you must, Node. Rails 5 is going to fit those use
cases just fine. Web programming is entirely too complicated to roll your own
stack unless you absolutely need to.

------
swagmeister
>Finally, JavaScript handles the modern web well, but it isn't perfect. If a
request does something that is especially CPU-heavy, every single user of your
application will be waiting.

One thing you might want to take into account is that ES2015 generators allow
you to write long, blocking functions as generators that partially compute the
result before deferring further computation to somewhere further along in the
message queue. This allows you to spread out blocking computations so that you
can still serve requests.

------
dschiptsov
Erlang is well-researched, pragmatical language (read the Armstrong Thesis at
last) this is why it is "great". User-level runtime, which is trying to do
kernel's job is a problem, but relying on message-passing and being mostly-
functional it, at least, have no threading/locking problems - so it scales.
Nothing much to talk about.

Go is also well-researched language with emphasis on keeping it simple and
being good-enough _and_ doing it right way (utf8 vs. other encodings) - a
philosophy from Plan 9. Go has in it a lot of fine ideas, attention to details
and good-enough minimalism - the basis for success. It is also pragmatic -
that is why it is imperative and "simply" static-typed.

Criticism about lack of generics or is not essential, especially considering
that generics in a static typed language is an awkward mess. Complexity of its
user-space runtime is a problem, of course, but runtime is hard, especially
when it is not mostly-functional.

Go is in some sense "back to the basics/essentials" approach, not just in
programming but also in running the code, and even this is enough to be
successful.

BTW, its syntactic clumsiness and shortcomings (hipsters are blogging about)
came from being statically typed, just admit it. On the other side, being
C-like and already having C-to-Go translators opens up the road to static-
analyzing and other tools.

Go is the product of old-school (Bell labs) minds (like Smalltalk or Lisps)
not of bunch of punks.)

~~~
XorNot
Go's error handling leaves something to be desired. I find myself reinventing
exceptions a lot by just passing errors up through generic "error" return
types.

It feels like it would be a lot cleaner to add syntax support for exceptions
and call it a day.

~~~
alienasa
There is support for fully "exceptional" behavior. It's called "panic".

The Go language designers explicitly didn't include exceptions because
"coupling exceptions to a control structure, as in the try-catch-finally
idiom, results in convoluted code. It also tends to encourage programmers to
label too many ordinary errors, such as failing to open a file, as
exceptional" [1].

I agree with them. While handling errors everywhere is a little painful to
write, it enforces better practices by making you acknowledge that things
could fail and ignore, handle, or pass the buck.

[1] [https://golang.org/doc/faq](https://golang.org/doc/faq)

~~~
codygman
> I agree with them. While handling errors everywhere is a little painful to
> write, it enforces better practices by making you acknowledge that things
> could fail and ignore, handle, or pass the buck.

There are better ways of accomplishing the same thing though, one way is the
Either monad in Haskell. Here's an example I post sometimes comparing error
handling in Go to Haskell's either monad:

    
    
        func failureExample()(*http.Response) {
            // 1st get, do nothing if success else print exception and exit
            response, err := http.Get("http://httpbin.org/status/200")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response.Body.Close()
            }
        
            // 2nd get, do nothing if success else print exception and exit
            response2, err := http.Get("http://httpbin.org/status/200")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response2.Body.Close()
            }
        
        
            // 3rd get, do nothing if success else print exception and exit
            response3, err := http.Get("http://httpbin.org/status/200")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response3.Body.Close()
            }
        
        
            // 4th get, return response if success else print exception and exit
            response4, err := http.Get("http://httpbin.org/status/404")
            if err != nil {
                fmt.Printf("%s", err)
                os.Exit(1)
            } else {
                defer response4.Body.Close()
            }
        
            return response4
        }
        
        func main() {
            fmt.Println("A failure.")
            failure := failureExample();
            fmt.Println(failure);
        }
    

The equivalent Haskell code:

    
    
        failureExample :: IO (Either SomeException (Response LBS.ByteString))
        failureExample = try $ do
          get "http://www.httpbin.org/status/200"
          get "http://www.httpbin.org/status/200"
          get "http://www.httpbin.org/status/200"
          get "http://www.httpbin.org/status/404"
        
        main = failureExample >>= \case
          Right r -> putStrLn $ "The successful pages status was (spoiler: it's 200!): " ++ show (r ^. responseStatus)
          Left e -> putStrLn ("error: " ++ show e)

------
georgeg
I feel that this discussion would be incomplete without mentioning D-lang
([http://dlang.org](http://dlang.org)) and the vibed framework
([http://vibed.org](http://vibed.org)) as alternatives to the technology
platforms that the author mentioned.

------
bsaul
What about deployment ? How does it compare to "scp and i'm done" like in go ?

Also what about memory usage ? My latest go service was a 4 mo file with one
single json config file. It consumed less than 4 mo in RAM, which let me
deploy it on a base 500Mo instance with plenty of memory to spare.

~~~
dxhdr
I haven't used Elixir yet but deployment in Erlang isn't quite that simple,
though it's not bad with some of the recent tools like relx. Just musing here
but if you're dev environment is "scp and i'm done" then Elixir/Erlang are
likely overkill for the problem at hand (Python or Ruby would probably work
fine?). There is some base amount of complexity involved with working with
BEAM but the features you gain in return are worth the trouble.

~~~
biokoda
"scp and i'm done" is completely possible with Elixir/Erlang. It is how we
deploy lots of erlang pages. Copy beam files, have erlang check periodically
if any beam files have changed and reload them.

Using erlang releases is in no way mandatory.

~~~
dxhdr
Sure I suppose that's possible but it just seems a little gross. At least with
'scp binary' your first and all subsequent deploys use the same process so
tools like Ansible can help you out. I guess that's what I was referring to --
repeatable deploys; pushing a new a BEAM release package is a more involved
process than just with a single binary.

~~~
mmcclure
I totally agree, being able to just ship binaries around is fantastic. No
complex process necessary, things Just Work™.

However, if you're ok with a little extra process, Elixir allows for hot code
reloads. To be fair, most use cases probably don't need it, but it's a pretty
great option to have in your tool belt.

------
4ydx
Models get ugly with tags? You don't have to have them unless the names are
different.

This post is all about familiarity and the degree to which you are interested
in using a language (just like every other post of its kind).

------
joshbuddy
Web workers are real concurrency and afaik, there is no GIL that spans across
the main thread and the worker threads in any of the browser implementations
of web workers.

~~~
rubiquity
The limitation with Web Workers is that the main worker thread is the only one
that can interact with the DOM.

~~~
codesushi42
Why would you want multiple threads touching the UI? Android processes for
instance only have one UI thread.

~~~
rubiquity
Updating is different than reading. Having concurrent reads would be a good
thing.

~~~
codesushi42
I do not see a big benefit from that. Reading from the DOM will already be
fast and can be handled in one thread. And you won't have concurrent reads if
another thread is writing to the DOM.

I think the designers of web workers made this decision thoughtfully. Other
environments like iOS and Android only have one UI thread as well.

------
jbverschoor
Important to note is that Elixir is created y Jose Valim from plataformatec.
An important person and company in the Ruby community

------
stefantalpalaru
Surprisingly favorable benchmark: [https://github.com/mroth/phoenix-
showdown](https://github.com/mroth/phoenix-showdown)

and related HN discussion:
[https://news.ycombinator.com/item?id=8672234](https://news.ycombinator.com/item?id=8672234)

~~~
chrismccord
It gets even more interesting on bigger hardware. A user ran the benchmarks on
a 10 core Xeon, Phoenix got 180,000 req/s and peaked at 75% CPU, so IO was
still the bottleneck. We were also able to have ~600µs average latency with
that load.

[https://gist.github.com/omnibs/e5e72b31e6bd25caf39a](https://gist.github.com/omnibs/e5e72b31e6bd25caf39a)

------
andyl
Elixir and Phoenix are great for web-apps, REST API's and the like. Phoenix
channels make real-time push super simple and scalable.

------
EdwardDiego
> We've known for years that Ruby and Python are slow, but we were willing to
> put up with that. However, as we more and more turn servers into REST APIs
> that are supposed to deliver JSON to numerous clients, for many it is time
> to find an alternative.

> You see a lot of competitors trying to fill this void. Some people are crazy
> enough to use Rust or Nim or Haskell for this work, and you see some
> interest in JVM based languages like Scala or Clojure (because the JVM
> actually handles threading exceptionally well), but by and far the languages
> you both hear discussed and derided the most are JavaScript via node and Go.

Meanwhile the Java programmers just keep on delivering with Jersey, Spring,
Restlet etc. etc. so forth. Less blogging, more doing.

~~~
dxhdr
No time for blogging, too much boilerplate to write!

~~~
EdwardDiego
Come now, we have IDEs for that. But seriously, Java users are not stuck in
the 1990s, even if your gibes at its expense are, so perhaps you should
revisit it. :)

~~~
vorg
Not just IDE's but also scripting languages like Groovy (the scripting
version), its predecessor Beanshell, other less often used ones like Nashorn
and Xtend, and even Clojure which doubles as a systems language.

NB: By "Groovy (the scripting version)" I mean the component of Groovy which
was Beanshell with closures and collections syntax added, but before the meta-
object protocol was. So Groovy as used by Gradle rather than Groovy as used by
Grails.

