News for hackers. Stuff that matters.
Re:Two years of Elixir at The Outline by latch on Friday October 26, 2018 @11:01AM (#18309826)
I'm bullish on Elixir and I agree that ExUnit deserves to be singled out.

Whether you consider Elixir fast or slow probably depends on what you're coming from and what you're doing. Personally, I wish the performance story would significantly improve.

But, the reason I think Elixir should be your goto language comes down to process isolation and the ecosystem around it (the synergy of the runtime, libraries and language). If you're doing a pure web app, you might not fully leverage it. But, for anything that involves long-lived in-process data (like a workflow/pipeline, or persistent connections), the way to approach this in Elixir (with supervisors, processes, message passing and pattern matching) tends to result in highly cohesive and maintainable code. Message passing is a pretty effective antidote to coupling.

I just finished writing code that had to take a stream of unordered time-based data and dedupe it over a small window. I'm not overly pleased with the final result, but it's completely isolated from the much larger app it sits in, easy to refactor/rewrite, with no risk of someone taking a shortcut and just accessing some internal detail, and testable.

I feel like I wrote a microservice, but without any of the challenges of a separate app.

Re:Two years of Elixir at The Outline by derefr on Friday October 26, 2018 @01:11PM (#18311123)
> Personally, I wish the performance story would significantly improve.

Before asking for performance improvements, have you tried:

1. tuning the BEAM? It has a lot of knobs. For example, there's "initial heap size", which can effectively turn any subset of short-lived processes that would otherwise need to do GC into simple "allocate an arena, do work, deallocate the arena" workloads.

2. tuning your OS? WhatsApp had to tune their (FreeBSD) kernel quite a lot to get it delivering packets to the Erlang runtime efficiently.

3. disabling tracing support? Part of the reason ERTS focuses on being a bytecode interpreter, rather than an AOT compiler, even in production, is that this allows ops engineers to connect to production nodes and trace/debug/profile code that's not doing what they'd expect, watching what happens as real user data flows through it. Because of this, the interpreter is "instrumented" even when a "production" (embedded, etc.) profile is used to generate a release. If you aren't going to use this tracing support, though, you can tell the runtime to globally disable it—for big wins in interpreter speed. (I separate this from #1, because this is basically the last-resort BEAM knob to turn. You're throwing away a lot of cool stuff by doing this.)

3. using HiPE? Specifically, sticking `@compile {:erl_opts, [native: :o3]}` into a subgraph of your modules (like a parser, a lexer, and the code that calls them both) can yield major improvements.

4. really using HiPE? I.e. recompiling both OTP and Elixir, and your project, using `ERL_COMPILER_OPTIONS="[native,{hipe, [o3]}]"`. This makes some things slower, so it's not the default, but it can beat #3 for modules that make a lot of stdlib calls (e.g. anything that loops using calls to Enum/:lists.)

4. using NIFs? (I don't recommend going this route until you've found that all of the above fail to get you enough performance, because the above all let you continue to just write plain Erlang/Elixir, while this step requires worrying about FFI. But it can be a big win, where and when it makes sense, just like in any other interpreted language.)

If your Erlang code is still slow after all of those, then I'll be surprised and would like to hear your specific story.

Re:Two years of Elixir at The Outline by latch on Friday October 26, 2018 @11:28PM (#18314205)
Thanks. This is really helpful. I tried to think of some specific example. I think most of what I'd generally come up with is tied to the available datastructures. We recently tested some code to essentially do a group-by in code, with various aggregation functions, and it was considerably slower in Elixir vs D and C. Very simplified, something like:

   Enum.reduce(rows, %{}, fn [user_id, hits], agg ->
     Map.update(agg, user_id, hits, &(&1 + hits))
   end)

I didn't apply any of your suggestions.

If I had another performance-like related pain point, it's throwing multiple cores at some data. I think it's somewhat common to have a large piece of data that you want to concurrently access. For example, keeping a large suffix tree in memory for an autocomplete endpoint, and letting each request concurrently access it. This isn't really a specific performance issue, but it's a performance sensitive pattern that is very antithetical to everything Erlang. ETS has limited structures, requires copying of values, and has no atomic replace (so you can't build a new version in a temp table and just rename it).

Re:Two years of Elixir at The Outline by zinclozenge on Monday October 29, 2018 @10:29AM (#18327788)
Specifically for the suffix tree in memory, you would store the tree as a constant in a module and dynamically recompile at runtime. By doing so it puts the tree in a special heap that can be read by all processes and doesn't get copied around. There's a library called 'mochiglobal' that does that for you. Discord ported it to Elixir and called it 'fastglobal' (although their implementation can be sped up by about ~25%).
Re:Two years of Elixir at The Outline by elcritch on Saturday October 27, 2018 @01:28AM (#18314486)
Tryout using Rustler and passing Refs. It’s a handy combination and Rust avoids most memory management issues one would face with NIF’s in C. I’m working on some data calculation code and think I’ll go that approach.
Re:Two years of Elixir at The Outline by nivertech on Tuesday October 30, 2018 @11:35AM (#18337605)
I think most people would do 4 before 3 and even 2.

Most erlangers would not use HiPE in production.

Re:Two years of Elixir at The Outline by mercer on Saturday October 27, 2018 @05:04PM (#18317636)
> I'm not overly pleased with the final result, but it's completely isolated from the much larger app it sits in, easy to refactor/rewrite, with no risk of someone taking a shortcut and just accessing some internal detail, and testable.

The 'let it crash' is really something I've not encountered in any other programming language/environment.

I've got a constantly growing Elixir project that I use for scratching various itches. Quite often I'll prototype some new feature in some of my free time, and quite often what I come up with is pretty haphazard.

In some cases the stuff I have running crashes for the silliest of reasons, but the rest of the app is unaffected, so it doesn't matter. A look at my log often makes it clear that I forgot something as stupid as checking for a timeout to a HTTP request. I fix the issue, push it to the server, recompile in the IEx console (or Application.start()/stop()), and keep an eye on what happens next.

When I have a bit more time, I might actually take a more careful look at the code to fix some obvious issues that somehow haven't caused problems, or I might add some typespecs when things seem to have solidified, where Dialyzer/Dialyxir, via its 'success typing' (which I guess is basically gradual typing like TypeScript?) will probably help me avoid stupid mistakes down the road.

What I like is how organic the whole process feels. The godawful, badly thought out parts of my project don't cause the whole thing to crash, and the more solid parts can be augmented by a type system. It's an approach that I was previously entirely unfamiliar with.

Re:Two years of Elixir at The Outline by hderms on Friday October 26, 2018 @11:23AM (#18310080)
I can see the logic behind the statement 'message passing is a pretty effective antidote to coupling' but I personally subscribe to the idea that actors don't compose. In light of that I think use of actors and messages implies some level of coupling which may not be obvious. Actors can't be arbitrarily composed together without knowing which actors they will be interacting with, making their addresses available and understanding their protocols so they can communicate together. Functions have no such requirements so I don't totally understand how message passing implies less coupling than conventional function-based architectures
Re:Two years of Elixir at The Outline by lostcolony on Friday October 26, 2018 @12:01PM (#18310453)
I think you're assuming side-effect free functions (and apparently ones whose interfaces are automatically known, since the interface to a function is just like the message handling in an actor; in fact, Smalltalk, the original OO language, didn't have 'methods', it had 'message passing'). But, you can still write libs and share them in Elixir, to benefit from any argument you'd care to make re: functions.

Actors are all about managing state. It's when state starts leaking around or being modified unexpectedly ('wait, why is that being called there?!') that a more typical language (Java, say) starts to get tightly coupled, and sharing that state is the norm, and expected. And I agree, if you have pure functions, that issue is largely controlled for too. Just, that's a really hard paradigm to stay inside of and still be productive.

I think that's why microservices are even a thing; it forces you to decouple state from different 'functional' parts of your system; by creating separate systems you have to be very intentional about what state you share, and how you share it (database or redis or an API call or similar depending on need).

Actors do that automatically for you; the state is contained within them; there's no chance(1) it will accidentally be changed by some other process/code, and the 'controlling' process gets to decide what messages affect changes, and how those changes occur. Or, in other words, all state changes relating to a context happen in one place, rather than state being passed around to be changed anywhere willy nilly across contexts.

1 Technically there are system level things that allow you to munge state inside of an actor. This is more frowned upon than using reflection for arbitrary reasons in Java, however, so the only reason you'll ever do it is because you're debugging what's going on in something, or you have decided you absolutely -hate- your toes and need to footgun them off.

Re:Two years of Elixir at The Outline by latch on Friday October 26, 2018 @12:13PM (#18310575)
You can certainly achieve the same low coupling with anything. But ....

Messages tend to be lightweight. Often a symbol/atom "command", and a few scalar values. Like command line arguments. If the process needs more data, it sends its own message to the owner of said data.

With conventional functions and OO, you're often dealing with complex objects (with references to references ...) which often leads to breaking the Law of Demeter.

Message passing also gets your asynchronicity. You're also decoupled from specific runtime implementation (the receiving can be on a different machine, the semantics are the same). You can achieve the same without message passing, but it's certainly more baked-into the runtime/tooling/language.

Joe Armstrong's popular quote on this:

"The problem with object-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle"

Re:Two years of Elixir at The Outline by jerf on Friday October 26, 2018 @12:00PM (#18310435)
You can use functions without knowing their inputs and outputs, and your functions don't need environments that somehow allow them to lookup the other functions they're going to call?

Messages and function calls aren't quite isomorphic, but they're pretty close. (They get really close if you include coroutines and coroutine-like things, to cover the non-1-to-1 relationship between messages and replies that messages can have.) I'm not sure the difference can sustain the implication you're trying to draw. Especially if you include RPC in the function world.

Re:Two years of Elixir at The Outline by dnautics on Friday October 26, 2018 @11:59AM (#18310433)
Message passing is less coupled than functions or "objects" that share bits of state.
Re:Two years of Elixir at The Outline by presscast on Friday October 26, 2018 @01:38PM (#18311336)
I think the pain-point being designated by the OP is that the cure is worse than the disease:

    Problem = tight coupling
    Solution = x, where x => {indirection, implicit_coupling}
I'm not sure I agree the final judgement call, but the analysis is spot-on. Message-passing interfaces are generally difficult to discover and to debug, and require a lot of careful documentation, logging & instrumentation.
Re:Two years of Elixir at The Outline by dnautics on Friday October 26, 2018 @03:16PM (#18312019)
> Message-passing interfaces are generally difficult to discover and to debug, and require a lot of careful documentation, logging & instrumentation.

You just use the "most common structures". For example, Elixir has the Agent module, which encapsulates state, but lets whatever you're using interact with the state via lambdas (or passed functions).

Another common structure is the state machine. I was somewhat displeased with the complexity of gen_statem, so I wrote my own! It's about 50 lines of code, and can have plugins to do facilities like logging and debugging via a plugin-able interface.

Ultimately the real win with message-passing is not having to write spinlocks, semaphores, or mutexes.

Re:Two years of Elixir at The Outline by lostcolony on Friday October 26, 2018 @02:24PM (#18311638)
So you create a function around the message. It's not uncommon at all to have something like (pseudocode)

myActorModule.increment(ref, amount) -> ref ! {increment, amount}

myActorModule.syncIncrement(ref, amount) -> ref ! {increment, amount, self()} receive -> ok timeout 1000.

You can build out the message passing interface to an actor -as- functions, and even supply sync ones if you want. The actor implementation can be completely opaque to the caller; just the exported interface functions are defined, exported, and supported.

Re:Two years of Elixir at The Outline by ilovecaching on Friday October 26, 2018 @02:26PM (#18311658)
So, just to speak about the BEAM, which is the part of Elixir I am familiar with, I don't think any BEAM language will ever be a good goto language. The BEAM isn't good at general purpose computing. It's not particularly efficient at performing calculations or using memory. It's good at reliably performing as you increase memory pressure on your system (via message passing). You can scale a business to a decent size on a smaller number of fatter nodes (high RAM machines). Connections will keep pouring in and you'll get a consistent ms.

That said, you'll run into issues once you hit a certain level of scale. I've had to make patches to the BEAM just to keep us scaling, and I've never seen someone get away with stock dist. You pretty much have to rewrite pieces of OTP.

Now is Elixir ok for a CRUD app serving 10G of traffic? Of course... but this is also the age in which every language has a solution to the C10K(, C100K, and more in many cases) problem, and there are compelling reasons to write your CRUD app in Go and get better library support, static types up front (trust me, you want a static type system), and lower latency.

Personally, I'm bullish about Rust. With async/await landing, it stands a good chance of providing the same focus on safety and reliability while being infinity more flexible at what it can effectively. It's a better general purpose language. Also, the actor model (and CSP as well) aren't the end all be all of concurrency patterns. Rust only provides the building block (futures) so it's much easier to choose the concurrent pattern that fits best for your problem. You can sting a future using the actor model, so it's still an option, but you aren't limited to it.

Re:Two years of Elixir at The Outline by emerongi on Friday October 26, 2018 @02:46PM (#18311801)
Ok... yet 99% of development is trying to write bug-free and readable code and do it pragmatically.

Elixir gives you that plus a lot of goodies that are really hard to get in other languages. The creators of Erlang were really clever and in my opinion, so are the creators of Elixir. If I ever were "bullish" on a language, Elixir would be it.

Elixir is a very pragmatic language. It doesn't get in your way, but still forces decent habits and design on your application.

Re:Two years of Elixir at The Outline by dnautics on Friday October 26, 2018 @04:45PM (#18312582)
just a nitpick, elixir does not let you write bug free code, it lets you write buggy code and actually get away with it.
Re:Two years of Elixir at The Outline by ilovecaching on Friday October 26, 2018 @03:41PM (#18312192)
> bug-free and readable code and do it pragmatically

Erlang has historically been a bad choice for this, and there's been a lot of effort (dialyzer, records, maps etc) to make it better.

Dynamically typed languages are, in my experience, not worth the cost of maintenance and readability. In Elixir and Erlang it's possible to create function signatures that tell you almost nothing about the shape of the data you expect to pass in. This makes it really hard to collaborate at scale. Various attempts to enforce dialyzer use are usually met with 'we need to move faster' or 'we'll add them later'.

So if you want to write bug-free readable code, I think Rust is a much better choice. It's statically typed, and the addition of traits and lifetimes tell you so much about how a function or type will behave, and there's no need to sync specs or docs. The code doesn't lie.

Re:Two years of Elixir at The Outline by StreamBright on Friday October 26, 2018 @04:13PM (#18312387)
Exactly, this is why the most reliable piece of mission critical system was written in Erlang.
Re:Two years of Elixir at The Outline by SZJX on Sunday October 28, 2018 @05:14AM (#18319806)
I've hardly run into any bugs in Elixir. It feels almost like Haskell to me. Pure magic.

On the contrary I struggle to get even the most basic Rust program to compile, wasting hours before anything is done.

Re:Two years of Elixir at The Outline by camgunz on Saturday October 27, 2018 @12:36AM (#18314374)
> The BEAM isn't good at general purpose computing. It's not particularly efficient at performing calculations or using memory.

BEAM languages are fine, easily faster than Python, Ruby, PHP, and competitive with v8 in many cases. Furthermore anyone doing serious data processing in a web service uses a work queue, which can be serviced (if you so choose) by workers written in any language you like.

Re:Two years of Elixir at The Outline by jashmatthews on Saturday October 27, 2018 @05:45AM (#18315054)
Ruby has had a VM competitive with BEAM since Ruby 1.9. BEAM has a slight edge but Ruby also has JRuby with a real JIT and all the GC goodness of the JVM.

Modern Python, Ruby, PHP7 are all much faster than generally expected outside of microbenchmarks.

Re:Two years of Elixir at The Outline by dymk on Friday October 26, 2018 @08:48PM (#18313724)
Oh I remember you - you were in the other Erlang thread peddling Rust exactly the same way. You'd make general claims of authority about Elixir/Erlang and how there were all sort of vague issues with them, and then gave out blanket advice for how Rust would be a better option.

It was pretty annoying to read.

Re:Two years of Elixir at The Outline by lpgauth on Friday October 26, 2018 @10:58PM (#18314132)
What did you have to patch in OTP? Usually it's pretty easy to get your changes merged upstream... And there's plenty of companies running large Erlang projects without any custom patches.
Re:Two years of Elixir at The Outline by gfodor on Friday October 26, 2018 @03:25PM (#18312086)
Until someone re-implements the process isolation and other battle-hardened distributed systems characteristics of BEAM/OTP in a rust stack, it certainly seems like embedding Rust as NIFs into erlang/elixir seems like a nice option to make up for the perf deficiencies you mention (though doesn't cover the memory concerns.)

I have not tried doing it, but I'd imagine the safety guarantees of Rust are a nice thing to have to reduce the risks involved around NIF callouts. I believe Discord recently posted that they are doing this, and I'm interested in hearing about their results.

edit: Google is my friend. https://github.com/hansihe/rustler

Re:Two years of Elixir at The Outline by qohen on Friday October 26, 2018 @07:04PM (#18313338)
In case it's of interest, Sonny Scroggin gave a talk last March at Code BEAM SF (i.e. the rebranded Erlang Factory) on writing NIFs in Rust, if you're interested:

https://www.youtube.com/watch?v=F86JXaCPowo

(Sonny is a core team-member of the Phoenix framework).

Re:Two years of Elixir at The Outline by ilovecaching on Friday October 26, 2018 @04:06PM (#18312341)
I think they're being reimplemented in Kubernetes. Kubernetes provides scale-ability, fault tolerance, and the pieces you need to build robust distributed systems. There's no need for this from the language itself. It's actually disadvantageous to mix Erlang and Kubernetes, because Erlang really wants a stable set of node names.

It's not a good fit for the way in which we're doing fault tolerance in the cloud. Erlang focuses on not crashing the BEAM, by letting processes crash while allowing graceful degradation of service. Supervisors are akin to Kubernetes deployments. Processes are pods. The pods themselves should be designed to crash gracefully.

Re:Two years of Elixir at The Outline by arianvanp on Friday October 26, 2018 @04:19PM (#18312434)
BEAM on Kubernetes is certainly possible: https://github.com/bitwalker/libcluster just not by default
Re:Two years of Elixir at The Outline by StreamBright on Friday October 26, 2018 @04:12PM (#18312376)
actually no you do need a static type system (i dont trust you on that one, i trust the extensive studies instead) and Go would be the worst example of static type systems anyways.
Re:Two years of Elixir at The Outline by dnautics on Friday October 26, 2018 @04:49PM (#18312606)
not having a static type system in elixir is great. I have my deploy code which takes a certain type of map, but i can paralellize my integration tests by adding a test_id field to the map which is passed a UUID in my HTTP call, which then goes stores in the API harness an RPC'd link (erlang RPC is nice enough to message-pass correct proc id's) to a dynamically created mock of the component which cannot be parallelized. At any given time during integration testing 5-10 of these mocks are in flight on the backend.

You could probably do this with a static type system. I wouldn't want to. I'm not an "expert" elixir programmer, and I got this up and running in 2 hours.

Re:Two years of Elixir at The Outline by PopeDotNinja on Friday October 26, 2018 @11:23AM (#18310084)
> I feel like I wrote a microservice, but without any of the challenges of an separate app

You feel that way because you did :)

Re:Two years of Elixir at The Outline by qaq on Friday October 26, 2018 @02:55PM (#18311858)
once Pony matures might be a good option if you care about performance
Re:Two years of Elixir at The Outline by moreoutput on Friday October 26, 2018 @12:55PM (#18310980)
I keep wanting to move to Elixir but always go back to Erlang. Any advice?
Re:Two years of Elixir at The Outline by derefr on Friday October 26, 2018 @01:22PM (#18311211)
The advantages of Elixir over Erlang, for me, mostly come down to the modules in its stdlib and package ecosystem.

If you don't enjoy writing Elixir, you could always just install Elixir, and then write Erlang that calls into Elixir-stdlib modules.

I believe you can even install Elixir packages into an Erlang rebar project, if you like. No need for Mix or anything.

Re:Two years of Elixir at The Outline by fcbrooklyn on Friday October 26, 2018 @01:11PM (#18311128)
Speaking as someone more into Elixir than Erlang, I'm wondering why you want to move to Elixir at all? If you're happier in Erlang, then why not stick with that? For me, the best thing about Elixir is the BEAM, and you've obviously already got that. If you don't want or need metaprogramming, or the various Elixir conveniences feel less than convenient to you, it seems like you may as stick with what you find comfortable.
Re:Two years of Elixir at The Outline by ams6110 on Friday October 26, 2018 @03:37PM (#18312165)
Why do you go back?