Jlouis is an Erlang programmer writing about building stable systems, Erlang internals, testing with QuickCheck, and functional programming.
Commenters recommend his articles on Erlang, including the stable systems guide and the Breaking Erlang Maps series, and link his explanations of Erlang misconceptions.
Author here. I haven't done too much with Elixir yet, other than this Phoenix app, but I am totally enamored of it from what I've seen.
First and foremost, the community is absolutely the friendliest, most helpful group of folks.
I'm happy with its performance. Phoenix feels similar to rails to me (I'm primarily a rails dev), but with easily 10X performance. And the concurrency. Oh, the concurrency. This is Erlang's bread and butter, and Elixir and Phoenix are built to take complete advantage of this. I love that every request gets its own Erlang process, and you don't have to worry about it blocking anything else. The Erlang scheduler will keep it fair, cutting off any long running computation if it has to, to keep the latency down on new requests.
I really like how interesting and mind bending Elixir's purely functional, process-oriented approach is. Nothing (well, very little) is mutable! You can sort of simulate mutation by spinning up a little infinitely looping tail-recursive process and sending messages to it. I encourage you to go through the excellent Elixir Getting Started guide [0], and in particular the "Processes" section for more on this.
But I think the thing I like most about it so far, is the introduction to battle-tested Erlang approaches to robustness (OTP). This is a set of abstractions and libraries that have been iterated on over the years and are a fantastic way of building an app that's resilient to failure. Elixir, as it does, takes these…
> Let me put it this way. In the last say five years, I can't think of a single article I've come across where someone describes DTrace making their life better (except one I wrote in 2011: http://blog.reverberate.org/2011/05/using-dtrace-on-os-x-to-...), or a single time anyone I know or work with has even mentioned DTrace.
Then you haven't been looking:
http://dtrace.org/blogs/brendan/2012/08/09/10-performance-wins/
http://dtrace.org/blogs/brendan/2014/02/11/another-10-performance-wins/
https://www.joyent.com/blog/node-js-in-production-runtime-log-snooping
https://twitter.com/b0rk/status/681536144362848257
https://speakerdeck.com/sartak/dtrace-war-stories
https://www.bignerdranch.com/blog/hooked-on-dtrace-part-1/
https://medium.com/@jlouis666/erlang-dirty-scheduler-overhead-6e1219dcc7#.mppk9kx55
Seriously, it wasn't that hard to find this stuff. I found the last one by searching HN:
https://hn.algolia.com/?q=dtrace
Like others who replied to you, I use DTrace every day. Very often, it solves in seconds what would have taken hours without it. Frequently, it's helped solve problems I'd never have been able to understand without it. My colleagues use it all the time as well. Sometimes, I write about the general techniques (I wrote the "runtime log snooping" blog post above), and most of our bug reports in recent years are public, but we don't write about individual bugs all that often. (Seems like you wouldn't have run into them if we had, though.)
This unfortunately breaks down with (bad acting) NIFs. Thankfully you can mark 'em as the dirty evil little things that they are (with negligible overhead): ERL_NIF_DIRTY_JOB_CPU_BOUND. [1]
I implore anyone interested in Erlang or its surrounding languages, to read its source code. [2] More specifically, the BEAM. I'll warn you that it's very 80's hackeresque, but in a good way. Incredibly pragmatic. The way they achieve their world-class scheduling? Tip: not via a perfect theoretical design cooked up in some comp-sci lab. [3] If you've been following game engine design within the last 9 years or so, you'll probably have a good idea of how they do it. [4][5]
[1]: https://medium.com/@jlouis666/erlang-dirty-scheduler-overhead-6e1219dcc7#.sakhllz5r
[2]: https://github.com/erlang/otp/tree/maint/erts/emulator/beam
[3]: https://hamidreza-s.github.io/erlang/scheduling/real-time/preemptive/migration/2016/02/09/erlang-scheduler-details.html
[4]: http://blog.molecular-matters.com/2012/04/05/building-a-load-balanced-task-scheduler-part-1-basics/
[5]: There's a hell of a lot of depth to this problem domain. Charles Bloom's guiding principles for Oodle are spot, and well worth a look.
> My understanding is that if I still want to ensure that the Beam VM can continue to schedule all processes efficiently
It's worse than that, overlong NIFs can lead to scheduler collapse with your schedulers being put to sleep and stuck there.
> these NIFs shouldn't calculate forever, but return quickly to avoid blocking all other processes.
Recent BEAM (R17 and later) has the experimental feature of dirty schedulers which allows running long NIFs without risking scheduler collapse. There is one set of dirty schedulers for IO-bound NIFs and one for CPU-bound NIFs, they should be marked accordingly.
> I've no idea how big the overhead actually is, I'd be interested in a rough estimate. Is it small enough that I can just ignore it entirely?
https://news.ycombinator.com/item?id=10141945 and the linked article
https://medium.com/@jlouis666/erlang-dirty-scheduler-overhead-6e1219dcc7 measure regular nifs at ~1.2µs and dirty nifs at ~10µs by default though they can be as low as ~3µs for core-bound schedulers and tuned wakeup options.
> Dealing with strings is not fun right now.
And dealing with strings isn't fun in many other languages or runtimes or OSes.
e.g.1. C# "Strings in .NET are stored using UTF-16 encoding. UTF-8 is the standard for Web protocols and other important libraries. Beginning in C# 11, you can add the u8 suffix to a string literal to specify UTF-8 encoding. UTF-8 literals are stored as ReadOnlySpan<byte> objects" - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types
e.g.2. Erlang/BEAM/Elixir: "The Erlang string type is implemented as a single-linked-list of unicode code points. That is, if we write “Hello” in the language, this is represented as [$H, $e, $l, $l, $o]". The overhead of this representation is massive. Each Cons-cell use 8 bytes for the code point and 8 bytes for the pointer to the next value. This means that the 5-byte ASCII-representation of “Hello” is 5*16 = 80 bytes in the Erlang representation." - https://medium.com/@jlouis666/erlang-string-handling-7588daad8f05
I feel like this kind of intensely provably-correct language (this one, Coq, Agda, etc.) isn't so much for writing whole programs in, as for writing the small, very-important-to-verify modules, that will link with other, less important modules to form a whole program.
When you're writing provably-correct "error kernels"[1] for your software, you probably want as little interaction with the OS to happen inside the kernel as possible. Write the rest of your program in a glue language, then hand off to the proven-correct error kernel for the hot loop. Probably over stdio, or transactional SHM IPC, or any other mechanism like that that has as little failure-surface as you can get away with.
[1] https://medium.com/@jlouis666/error-kernels-9ad991200abd