Table of Contents for Programming Languages: a survey
Haskell
Because it is so well-liked, Haskell gets its own chapter.
See chapter on chapter on Haskell
Scala
Scala is a multiparadigm language, both OOP and functional.
Tours and tutorials:
Cons:
- big language
- slow compilation
- culture of punctuation-based DSLs can make readability hard
Best practices:
Tutorials and books:
Comparisons:
Features:
- higher-kinded types
- implicits
- " Scala is completely interoperable with Java (and with some qualifications also to C#). A Scala component can: • access all methods and fields of a Java component, • create instances of Java classes, • inherit from Java classes and implement Java interfaces, • be itself instantiated and called from a Java component. " -- [1]
- "Scala’s concepts subsume SML modules. More precisely, (generative) SML modules can be encoded inνObj,but notvice versa." -- [2]
- Abstract Types " Here is a type of “cells” using object-oriented abstraction. trait AbsCell? { type T val init: T private var value: T = init def get: T = value def set(x: T): Unit = { value = x } }
The AbsCell? class has an abstract type member T and an abstract value member init. Instances of that class can be created by implementing these abstract members with concrete definitions.
val cell = new AbsCell? { type T = Int; val init = 1 } cell.set(cell.get * 2)
The type of cell is AbsCell? { type T = Int }.
Path-dependent Types: You can also use AbsCell? without knowing the specific cell type:
def reset(c: AbsCell?): Unit = c.set(c.init);
Why does this work?
- c.init has type c.T
- The method c.set has type c.T => Unit.
- So the formal parameter type and the argument type coincide.
- c.T is an instance of a path-dependent type.
In general, such a type has the form x0...xn.t, where
- x0 is an immutable value
- x1, ..., xn are immutable fields, and
- t is a type member of xn " -- [3]
Some notes on Scala 3 features:
example of scala 3 'givens' which are (part of) the successor to scala implicits:
https://alvinalexander.com/source-code/scala-3-dotty-complete-givens-using-example/
so afaict these are just similar to Haskell typeclass instances. I guess what scala is really contributing is that i think these are lexically scoped, rather than globals like (i think) in Haskell.
Extensions:
Retrospectives:
Implementations:
Opinions:
- "Scala is the single best language I've ever used. And I don't even use all of it. I just use the subset of Scala that is Everything Java Should Have Been. It has lambdas and closures. It has just enough type inference. It has mutability control for slots. It has object orientation and case-classes/pattern-matching and type-classes via implicit parameters. Scala lets me figure out what language paradigm I need to Get Stuff Done. It's the Python of statically-typed languages, and it interoperates with a vast outside ecosystem to boot." -- [5]
- "I do want a functional language like Scala, because I believe that school represents a better design. That school of language design saves me the headache of null pointers; it helps me write unit tests with QuickCheck?; it allows me to define custom generic collections; it places emphasis on having a fast GC; it features monadic style to help with error handling." -- [6]
- [7]
- "...problem with all of these languages is that they are still obscure compared to everything else, with all that this entails: It's harder to find developers...Scala has some good parts, but is overdesigned and way too complex, in my opinion. Last I checked, performance was worse Java. It also runs on the JVM, which means its memory usage is inherently inefficient." -- [8]
- "Scala has always seemed like an abomination to me, but I haven't used it enough." -- [9]
- http://grundlefleck.github.io/2013/06/23/using-scala-will-make-you-less-productive.html
- "Scala's the only one of these three languages with any adoption right now. Groovy is virtually only used in its original dynamic mode from 2003 for things like scripting and Gradle build files, and very few people use the @CompileStatic? mode introduced in 2012. Kotlin 1.0 has just been released, was subjected to stringent QA, and I expect JetBrains? to use it more and more in its own products like IntelliJ?, so it's a good bet it will become more popular with developers. So I'd say use Scala and Kotlin if you need static typing. Clojure, though it doesn't have static typing, has other features like syntactic macros for terseness, default immutability, and concurrency that would recommend it in many situations." [10]
- https://news.ycombinator.com/item?id=11678751 : "
- "While the fusion OOP + FP fusion is interesting I have grown wary of kitchen sink languages. There are so many ways people write Scala. " [11]
- Lends itself to DSLs (some people think this is good, others thing it is bad [12])
- "I remember when I first saw the potential issues of scaling Scala at Gravity back in 2009/10ish. It was close to the end of the day when we had a major issue reported in production that was affecting our large customers. Several of us started investigating and were able to track the source of the issue. The only problem was we had no idea what the code was doing at first. We came across a strange symbol we hadn’t seen in our projects before. The spaceship operator <
- slow build times
- "Scala has had big issues with the size of it's library that still haven't been resolved" [14]
- "I worked on a commercial project that used Scala. Never again. Compiles were slow, IDE support was terrible, everyone had their own subset they used. I'm sure it's improved since 2010. " [15]
- "The language and even the basic libraries are incredibly complex. Here's an example, a signature for List.map: final def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom?[List[A], B, That]): That" [16] (other comments in that subthread explain what that signature means)
- vs Kotlin: https://elizarov.medium.com/why-im-not-enthusiastic-about-java-10-b2d789b6d42a
- Because all the other languages mentioned are better only from very specific perspectives. Scala's strength is precisely that it's a mutt: Want to write imperative code? Sure. Functional? No problem. How about a type system that far more featureful than Go? That works too.
Scala gets a bad rap precisely because people are big fans of their own way of writing code, call it better, and think that anyone that wants something else is deluded. The Scala way is to say 'sure, you can do that too, to hell with purity'. I'd rather have power over purity any day.
reply
duaneb 36 minutes ago
> Scala's strength is precisely that it's a mutt: Want to write imperative code? Sure. Functional? No problem. How about a type system that far more featureful than Go? That works too.
This also essentially describes C++. This is also why both languages can be so terrible to use: every library has its own dialect.
reply
the_af 20 hours ago
Note: I use Scala in my day job. I consider it better than Java, but worse than other languages. I definitely agree that all languages accumulate compromises and inelegant hacks as they evolve. It's unavoidable.
That said, in my opinion Scala's blunders are many. You can find out about many of them from Paul Philips, ex committer of Scala, but if he sounds too bitter to you (he does to me; at this point he sounds like there is bad blood between him and Odersky), here are some of mine:
- Null. Null will come back to bite you in Scala code, whenever you call Java libraries, and the occasional Scala library that didn't get the memo. Sure, you can wrap every suspect value with Option(...), but why should you do this?
- Type inference is not as powerful as in a language with a cleaner type system such as Haskell. I'm told this is because Scala tries to be both OOP (inheritance) and FP at the same time. Here is a clear case of Scala's attempt at being a "jack of all trades" resulting in it being inferior than the sum of its parts.
- Type signatures in Scala collections and core types are extremely hard to read. You do not need to read them, but if you try, you are in for a world of hurt. This in itself is not a mortal sin, but of course if you're willing to forgive Scala this much complexity, surely you're willing to forgive other languages as well?
- Tooling is bad. It's getting better, but it's still bad. SBT is painful to use. As for IDEs, IntelliJ? is now the recommended choice; I've tried both Scala IDE and IntelliJ?, and they both suck. Slow, spurious compilation errors, uncomfortable to use.
- And finally, telling bit of personal experience: I do NOT use Haskell professionally, yet whenever I want to try a quick idea and see if it typechecks, I find it easier to open ghci (the Haskell REPL) and try my idea than do the same with the Scala REPL. What does this say about Scala?
reply
airless_bar 20 hours ago
> all languages accumulate compromises and inelegant hacks as they evolve. It's unavoidable.
Scala devs deprecate and remove things that haven't worked out well. They have an established track record of making these migrations easier with each release.
> Null
Upcoming versions will make references non-nullable by default. If you want things to be nullable, you will have to opt-in explicitly with T
> - Type inference
Type inference is not a huge priority, because people think that it's a good thing that declarations require type annotations (just like it's recommended in Haskell, too). One of the last pain-points, higher-kinded unification of type constructors, has been fixed recently which will make type inference "just work" in exactly those cases where the lack of type inference was most annoying.
> - Tooling
I think tooling is pretty amazing. Sure, Java has still some advantages in some places. But the tooling is superior to almost all other languages out there. SBT is amazing. IDE support is getting vastly better, not only have Eclipse and IntelliJ? improved vastly, but also IDE support for Emacs, Vim, Sublime, etc. is coming along at a rapid rate, and it's just amazing to use. There are so many tools in the Scala ecosystem which just don't exist in this combination anywhere else.
airless_bar 19 hours ago
Alright. Sorry.
Regarding your question about nulls and Java:
I think that there is not much Scala can do here. All existing evidence from other languages that tried this shows that there is a large semantic gap between "nullable values" and "values of unknown nullability".
As Scala is much more independent of Java it is a much smaller issue, but improvements with Java interop require action from Java library authors first.
The approach of supplying external nullability meta data has largely been a failure, because
a) authors are not aware of the stated, external constraints, so they could break these assumptions in future releases without even knowing
b) it's really really hard to retrofit nullability guarantees into libraries which have been designed without this requirement in mind
c) the existing ecosystem is not very amenable to these guarantees, as nullability metadata would be largely limited to final classes and members, because everything else could be subclasses or overridden in third-party code, breaking these assumptions
As soon as Java library authors themselves start using @Nullable/@NonNullable? annotations everywhere, there is a good opportunity of reading these annotations and typing the values accordingly, but without it, benefits are very slim.
The planned changes are valuable for dealing with nullable types, but as mentioned "unknown nullability" needs a different solution, and I think it's currently not worth adding something to the languages as long as there is still hope for widespread adoption of nullability annotations.
reply
bad_user 17 hours ago
- I've never experienced null problems in Scala. There's theory and there's practice. In practice if a NPE does happen, you treat it as a bug and wrap it. I don't experience problems, because Scala libraries are well behaved and for Java libraries I read the docs.
- Being a "jack of all trades" means Scala has the superior module system. In Scala you can have abstract modules, the way you have in Ocaml. In Scala type-class instances are lexically scoped, whereas in Haskell they are global. Haskell's type-classes are anti-modular, which is why there are people avoiding type-classes. A big part of what makes Scala so good is OOP. Haskell needs extensions to achieve similar functionality in a half-baked way and modularity is the main complaint of people coming to Haskell from Ocaml.
Btw, if you ask a C++ developer to describe OOP, he'll say it's the ability of having structs with a VTable attached. Most people think OOP is about state. That's not true, OOP is about single (or multi) dispatch and subtyping (having a vtable), hence it's not at odds with FP, unless you want it to be ;-)
- SBT is amongst the best build tools ever available. I could rant all day about the clusterfuck of Javascript (npm, Bower, Grunt, Gulp, Brunch.io, etc.) or Python (easy_install, setuptools, virtualenv) or .NET (MSBuild, Nuget, dotnet) or Haskell (cabal). For all its quirks, SBT is by far the sanest dependency and build management tool I've worked with. In fact, amongst the best reasons for preferring Scala.js is being able to work with SBT and avoid Javascript's clusterfuck completely.
- I've been working with IntelliJ? IDEA with the Scala plugin for the last 3 years. I've had some problems with it, but all minor and it's amongst the best IDEs available, giving you everything you expect out of an IDE. Other platforms either don't have an IDE (Haskell), or require extra commercial plugins to behave like an actual IDE (Visual Studio).
And let me give an example: in IntelliJ? IDEA I can click on any identifier in any third-party dependency and it automatically downloads and shows me the source code and I can set breakpoints and debug any third-party dependencies that way. Such a simple thing, yet it's a tough act to beat. Try it out in Visual Studio sometimes.
reply
asragab 16 hours ago
What languages would you say have definitely better tooling: I'd say for sure the .NET languages, C++, Java, and Javascript but I'd put Scala just at a level below that, no? The tooling stories for Go, Rust, Haskell I don't think are as strong (yet), but that's definitely going to change though.
reply
airless_bar 16 hours ago
Not parent, but I would reduce it down to
- C# with VisualStudio? + JetBrains? addon
- Java with IntelliJ?
Why not the others you mentioned?
- IDE support for F# is not very good.
- VB is too dynamically typed to be reliable.
- C++ IDEs seem to be constantly fighting with constructs that manage to break the IDE's understanding of the code. It's gotten better with LLVM, but even VisualStudio? is far away from providing a reliable experience.
- JavaScript? is dynamically typed, so IDE support is not reliable.
reply
pjmlp 11 hours ago
- VB.NET support is at the same level as C#
- F# does lack some Microsoft love, but Visual F# Power Tools does improve the experience a lot
- Netbeans and Visual Studio 2015 are quite good in JavaScript? support, specially on code where JsDoc? comments are available
reply
"
- "The main problem of Scala is that it brings in the whole JVM. That's awesome for certain kinds of apps, but not so great for other things like small tools. I have high hopes on scala-native, though!" [17]
- http://matt.might.net/articles/best-programming-languages/#scala
- "You want a statically typed functional (but not pure) language with sum types, immutable data structures, good performance, editor/IDE support, a broad library ecosystem, multithreading, a C-like syntax (I assume, since you're using ReasonML?), and a nice compile-to-Javascript experience to run in the browser. I don't want to sound too much like a fanboy, but all this really sounds like Scala. You want Scala." -- lihaoyi
- "...Scala has a similar thing for this where sometimes Eta expansion happens and sometimes it doesn't, and you just need to know where you can use sugar." -- [18]
- "...I miss relatively basic things like c-style for loop (so I can use a for loop on linked lists rather than a while), or things like a list that can have more than Int.MaxValue? numbers. " [19]
- "Always struck me as a functional language that was more complicated than it had to be because it needed to interoperate with the existing Java ecosystem, run on the JVM, handle Java exceptions, etc." -- [20]
Opinioned comparisons:
- "I like Scala too. But it lacks some things that Go has, and help me in a few cases: no static binaries. No control over memory alignment. No dead easy FFI to C. No dead simple syntax I can understand in a weekend." -- [21]
- https://www.reddit.com/r/haskell/comments/3h7fqr/what_are_haskellers_critiques_of_scala/
- "Being a "jack of all trades" means Scala has the superior module system. In Scala you can have abstract modules, the way you have in Ocaml. In Scala type-class instances are lexically scoped, whereas in Haskell they are global. Haskell's type-classes are anti-modular, which is why there are people avoiding type-classes. A big part of what makes Scala so good is OOP. Haskell needs extensions to achieve similar functionality in a half-baked way and modularity is the main complaint of people coming to Haskell from Ocaml." [22]
- https://movio.co/blog/migrate-Scala-to-Go/
- http://jimplush.com/talk/2015/12/19/moving-a-team-from-scala-to-golang/
- "Do I miss immutable types and some of the great features of Scala? Sure do, but I think the maintainability side of the story is too great to overlook with Go. We’ve seen faster ship times, better stability and better test coverage being written." -- [23]
- "At it's core Scala is very simple & the syntax is very regular, far more than Go or Java and a lot less complex than C++." [24]
- "I liked it more than Java (7), though I find the syntax aesthetically offensive (and I realize that's subjective). Ada (I worked in an Ada shop for 3 years before moving to the JVM) managed to have a robust type system without introducing the sort of syntax wtf-ness that Scala seems to need. I actually recommended against using Scala at a later job simply because I thought the learning curve would be beyond most of the people I was working with (I didn't phrase it quite like that when mgmt asked me for my opinion, of course). Learning to write idiomatic Scala takes time. " [25]
- http://thume.ca/2019/04/29/comparing-compilers-in-rust-haskell-c-and-python/
Scala examples
" To give a feel for the language, here’s a Scala implementation ofnatural numbers that does not resort to a primitive number type.
trait Nat { def isZero: Boolean; def pred: Nat; def succ: Nat = new Succ(this); def + (x: Nat): Nat = if (x.isZero) this else succ + x.pred; def - (x: Nat): Nat = if (x.isZero) this else pred - x.pred; }
class Succ(n: Nat) extends Nat { def isZero: Boolean = false; def pred: Nat = n }
object Zero extends Nat { def isZero: Boolean = true; def pred: Nat = throw new Error("Zero.pred"); } " -- [26]
(S)ML
Pros:
Tutorials and books:
Comparisons and opinions:
- http://thebreakfastpost.com/2015/04/22/four-mls-and-a-python/ vs other MLs, Python
- "...all the extant MLs fall short in one way or another. A modern SML with none of the syntactic cruft of OCaml; the lazy evaluation of Haskell and its non-existent module system; the nutured nature of F# ... that would be ML joy, maybe Rosenberg's 1ML will make that a reality." -- https://news.ycombinator.com/item?id=10210679
- vs Haskell: "From one view, ML is the worse language, but from other views it would be Haskell. Suppose you require a fully formal specification which has been verified in a mechanized way. You have that for Standard ML in Twelf and Haskell has nothing sort of that. If you look at the concept of being a purely functional language however, it is the opposite with Standard ML being the "worse" animal. Module system: Then SML got it right and Haskell got it Wrong. Laziness/Strictness: This is a duality. There are advantages and disadvantages to both approaches so there is no worse/right choice IMO. If you look at the recent stuff on polarity in proof theory it becomes clear that when you latch onto a specific evaluation order, you make some things simple and other things hard." [27]
- What's Wrong with C++ Templates? compares C++ templates to Java interfaces and ML modules
- http://matt.might.net/articles/best-programming-languages/#ml
- https://blog.plover.com/prog/haskell/sml-defects.html
Wishes:
Implementations:
Links:
Variants:
Retrospectives:
Ocaml
Tours and tutorials:
Features:
Library recommendations:
Retrospectives:
Updates:
Opinions:
- "general-purpose language...good predictable performance...strong type system...the last thing, which I think is more unique to OCaml or at least more unique to the MLs, is that OCaml takes modularity very seriously" -- Leo White
- "One of the main reason I got into Ocaml (coming from C++) was that it had bytecode compilation (and obviously a bytecode interpreter) next to the native one. This is really fast (5-7 times faster last time I measured) and perfect during development, when you don't really care about performance. " -- [29]
- "I wanted to use OCaml several types but the lack of parallelism and the GIL have put me off. Still waiting for Multicore OCaml." [30]
- "Its compiler is actually very good (when it comes to emitting correct code, being fast, and having few compiler bugs). What is lacking is indeed a few “modern” things: no standard deriving/serde (where rust clearly shines) bigints are not the default (zarith is neat but it’s not as seamless as int) the portability/static binary story is lacking. It’s really sad, because OCaml with some of Go’s tooling qualities would be incredible. " -- [31]
- " Yeah, OCaml is frustratingly close, but I also have a few gripes with its inner workings: objects are redundant polymorphic equality/hash/comparison is awkward, which is a sign of a deeper ad-hoc polymorphism issue compilation model with a shared global namespace and without explicit DAG of dependencies is pretty horrible (ocamldep is one of the grossest hacks in my book) no mature multicore support " -- [32]
- " ocaml objects aren’t any more redundant than any feature in any language that has more than one way to do things. they may not be much used due to cultural/ecosystem choices, but every now and then they are the most ergonomic solution to what i’m trying to do. the namespace issues are indeed a pain though :( after getting a day job working in python i have been dissatisfied with namespacing in both ruby and ocaml, which are otherwise two of my favourite languages. " -- [33]
Ocaml Opinions:
- Q: "why (does) so much symbolic evaluation stuff gets done in OCaml? What does OCaml do that makes it so well suited for this problem domain?"
- A: "...pattern matching is really nice for this kind of thing. Of all of the functional programming languages, OCaml has probably the most sophisticated pattern matching engine around (and we basically copied it into Rust, incidentally), supporting or-patterns, multiple bindings, guards, and so forth. Pattern matching lets you essentially match on the shape of subtrees of arbitrary data structures with complex predicates. If you're familiar with old-school compiler construction, this is like having a souped-up BURG built into the language. For example, pattern matching lets you say things like "if I have Load(Var, Add(Var, Constant)) where constant is a small power of two, fold it into the x86 indexed addressing mode" in one line...." -- https://news.ycombinator.com/item?id=9701011
- A: "...pattern matching, ADTs and higher-order functions are ideal for symbolic manipulation. Nowadays many languages have that (Scala, Rust, F#, Haskell), but Ocaml is old and was probably the first language that offered these features and had a really fast, rock solid compiler producing really fast, high-quality code (since the 1990s)." -- https://news.ycombinator.com/item?id=9702675
- A: "Symbolic reasoning involves a lot of term-matching and term-rewriting. These tasks are best accomplished in programming languages that have first-class support for recursive terms (aka. algebraic data types). Popular candidates these days are OCaml, Haskell, Scala." -- https://news.ycombinator.com/item?id=9700976
- "Here are some examples of what is possible with symbolic manipulation in OCaml, although I would recommend learning a bit of OCaml syntax and concepts from a book first (such as Real World OCaml):
- A short example of implementing regular expression matching using Antimirov's partial derivatives that illustrates symbolic manipulation: http://semantic-domain.blogspot.ro/2013/11/antimirov-derivatives-for-regular.html . A step-by-step explanation of a DSL optimizer that doesn't use too many advanced notions of OCaml: http://okmij.org/ftp/tagless-final/course/optimizations.html . A nice example of symbolic manipulation is this counter-example generator for regular expression equivalence: http://perso.ens-lyon.fr/damien.pous/symbolickat/ " -- https://news.ycombinator.com/item?id=9701756
- "I'd characterize OCaml's tools as a mixture of best-of-breed (Merlin, OPAM) and oh-my-god (build tools, debugger). The language itself, if you avert your eyes from the standard library, is pretty damn good though." -- * "One of the biggest problems now is the [https://github.com/ocaml/opam/issues/246 lack of the Windows support for Opam. I hope that will be solved in the near future. Because currently Microsoft trying to take the niche with their F#." -- [34]
- "OCaml is super-practical for writing simple command line tools. It interfaces easily with C and compiles to binaries with no dependencies. See: all the tools written in OCaml here: https://github.com/libguestfs/libguestfs " -- [35]
- "...problem with all of these languages is that they are still obscure compared to everything else, with all that this entails: It's harder to find developers...OCaml looks to me like the most promising functional language with the fewest warts, the most pragmatic approach to real-world apps, and possibly the best performance. But it's not a simple language, with a learning curve that's similar to Haskell. I also have to say that like Erlang, the toolset and syntax occasionally feels antiquated (for example, there's no excuse not to provide a modern readline-enabled REPL). OCaml still is not good at concurrency." -- [36]
- "Ocaml is also a great language but it had problems with poor standard libraries and concurrency support." -- [37]
- OCaml for the Masses: Why the next language you learn should be functional
- http://www.podval.org/~sds/ocaml-sucks.html
- "...I have been absolutely blown away by the amount of power this language provides. It strikes a nice balance between high-level expressiveness, while not sacrificing on performance. I feel like OCaml is one of the programming world's best kept secret - if it had better tooling and better marketing, it could have taken over the world. I'm cautiously optimistic about ReasonML? for this reason." [38]
- " Version incompatibility is a big problem for OCaml. Every time I try to use it seriously, the recommended tools never seem to work together on anything but the latest version. It's like Rust, but without the "young language" excuse. My impression is that INRIA is too eager to add features and tweak the language. Next, everything builds so slowly, and just trying to set up Core, utop, and ocp_indent is a trial in patience, watching the same dep build again and again, confusion about OPAM switches.... nope nope. OCaml/OPAMs's got a distribution problem. They've pushed the complexity off to the end user and the experience is terrible. Jane Street planting their flag on the language and encouraging massive dependencies like Core, or shit like "corebuild" is even worse. I would never depend on anything OCaml-based on it unless it could be installed by my OS' package manager (Arch/AUR doesn't count, because it builds). That rules out most popular OCaml tooling, and ruins the development experience. I'd prefer OCaml to Haskell, I find it more practical, but I feel much better depending on Haskell-based software, which generally "just works." As much as they're a wart, Haskell syntax extensions are a real benefit. You can build new fancy Haskell software with older GHC versions." [39]
- https://news.ycombinator.com/item?id=7769404
- "there is not much manpower behind it. All those Reason XYZ attempts to provide javascript syntax over it don't seem to have gotten traction. Tooling is pretty good considering how obscure language is. They might need a modern interface to toolchain, and an optimizing compiler seems to be being worked on. La k of multicore is often cited as a drawback but it is being worked on, and Python doesn't do multicore either, I don't think multicore matters to 90% of people doing webdev." -- [40]
- "Keiichi once told that one of the reasons they could grow Ucc so rapidly was that they wrote Ucc by OCaml. OCaml allows you to manipulate tree structure so easily without any pointer bugs." -- [41]
- "...in OCaml you have opam, esy, dune, and OCaml itself all doing part of the job. They sorta kinda mostly work well together, until they don't. And as you wire them together, you really need to understand what each tool does, where it starts and stops, and how they integrate. Honestly, not fun at all." -- Paul Biggar
- vs Rust: "Rust feels really well designed, which I expect is because they've aggressively pruned the parts of the language that don't make sense. So the Rust that I see today is supposably quite different from the Rust of 2010. Meanwhile, OCaml has a ton of stuff that they really should prune and have not." -- Paul Biggar
- vs Rust: "...Rust the language is quite pretty, syntax-wise. Meanwhile, OCaml is so ugly that the community came up with a whole other syntax for it. This shouldn't be a big deal, but as someone responsible for getting folks up to speed on OCaml for a few years, this was not a fun experience." -- Paul Biggar Library tours and best practices:
- "There are least three competing standard libraries: Jane Street's Core (high-quality but fairly big, also generates large binaries due to OCaml's lack of good dead code elimination support), Batteries (older, developed by the community), and Containers (Simon Cruanes' project, mostly - smaller but very well-designed). They all have their advantages, the issue is that there is no one true alternative standard library, which makes the life of library writers difficult (especially when you want to use a type in your interface that is only present in one of these libraries). " -- https://news.ycombinator.com/item?id=10210550
- "OCaml is a language whose Raison D'etre is to build interpreters and compilers" -- Paul Biggar
- a complaint about confusing error messages when functions have the wrong number of arguments: https://news.ycombinator.com/item?id=31860936
- https://batsov.com/articles/2022/08/29/ocaml-at-first-glance/
Ocaml Opinionated Comparisons:
- http://thebreakfastpost.com/2015/04/22/four-mls-and-a-python/ vs other MLs, Python
- "Other than OCaml and (maybe?) F#, I don't know of a language that has as sophisticated a pattern matcher as Rust does, which helps a lot with this stuff." -- https://news.ycombinator.com/item?id=9701086
- Q: " In what ways is OCaml's pattern matching superior to Haskell's? " A: "Or patterns. See http://stackoverflow.com/questions/24700762/or-patterns-in-haskell" -- https://news.ycombinator.com/item?id=9701347
- "On the flipside (of OCaml having OR-patterns and Haskell not), Haskell just got pattern synonyms which are incredibly useful for being able to refactor and work with abstract types. It also has view patterns which are quite useful. I don't know how to replicate that in OCaml. Combining the two lets types expose fairly sophisticated interfaces as normal patterns, which is incredibly useful for things like graphs." -- https://news.ycombinator.com/item?id=9701476
- https://www.reddit.com/r/ocaml/comments/3ifwe9/what_are_ocamlers_critiques_of_haskell/
- https://www.reddit.com/r/haskell/comments/3huexy/what_are_haskellers_critiques_of_f_and_ocaml/
- "One technical reason that makes me prefer OCaml to Haskell is Haskell's weak module system. I think there should be a good way to parameterize one module over another module (interface) and to use different instantiations of the module in the same program. I think people do such things using type classes and typing constraints. But I found this being awkward, because the compiler needs to be able to resolve the constraints automatically and there are issues with overlapping instances and the like." -- [42]
- https://www.mpi-sws.org/~rossberg/sml-vs-ocaml.html
- http://adam.chlipala.net/mlcomp/
- https://news.ycombinator.com/item?id=10209307
- Q: "I know Clojure, looking to learn a statically typed functional language. Narrowed it down to Haskell and OCaml (are there others I should know about?). I still am unsure what OCaml is good for. Ex., Ruby is good for webdev with Rails, Python is good as a general purpose language, Clojure is good for async and quick iteration/integrating with Java code, C is good for OS work, C++ is good for native applications. Is OCaml general purpose? Can I use it for NLP? Statistics? Numpy-like n-d array math? Writing a compiler?"
- A: "OCaml is super general purpose. You can write low-ish level code (there have been 2 kernels partially written in it) as well as higher level stuff (web servers/scripts on the pages using tools like js_of_ocaml, Coq, etc.). Where it really stands out it compilers and static analyzers (+ other similar things). Facebook uses it for a few of their compilers and static analyzers, the Rust compiler was originally written in OCaml before being ported to Rust, the Haxe compiler and VM are in OCaml as well. The OCaml compiler comes with lexer and parser generators and a lot of other libraries exist for it too (such as an official LLVM binding). I'm not sure how great it is for NLP, but I'd assume it's not bad for it. Here is a list of companies that use it: https://ocaml.org/learn/companies.html." -- [43]
- A: " Not currently an OCaml user, but it's definitely general-purpose. I don't think OCaml fits into a specific broad niche, although one of the areas where it sees more use is in computer language development/research. A whole bunch of compilers are implemented in OCaml (Hack and Flow by Facebook, for example), and then there are tools like Coq, and some static analysis tools (Flow is one, I guess) are also in OCaml. What I think you'll find is that the standard library is quite lacking, as is the availability of third-party libraries. The ecosystem is nowhere close to what it is for languages like Node.js, Ruby — or even Haskell. " -- [44]
- A: "I can't think of a better language to complement Clojure than OCaml. It hits all of Clojure's weak points dead-on, and its own weak points are covered really well by Clojure. In terms of "general purpose", I think of Clojure as being a better choice for long-running server processes and OCaml as being a better fit for code you run on your own machine or programs that don't stay running forever, due to the varying memory/performance/concurrency characteristics of their respective runtimes. I wrote in more detail about my experiences coming from Clojure to OCaml here: http://technomancy.us/170 I found it very easy to pick up with some FP background. Not having to think about nil in particular was a huge breath of air after Clojure. I also had good luck accepting contributions from others with a Clojure background who wanted to make some changes to my code and learned OCaml just for that." -- [45] More details: http://technomancy.us/170
- Ocaml from Python: http://roscidus.com/blog/blog/2014/02/13/ocaml-what-you-gain/
- Python can be less verbose than OCaml due to higher-kinded types and implicits [46]
- http://thume.ca/2019/04/29/comparing-compilers-in-rust-haskell-c-and-python/
- https://wiki.haskell.org/OCaml
- vs Haskell: "I've used both languages a lot. My current job is in Haskell and my previous (along with two internships) was in OCaml. With that experience, I would use Haskell over OCaml for basically everything. If you're curious about details, I wrote an in-depth post about it a few years back. Core points:
- Haskell is massively more expressive in practice. The OCaml code I worked on was routinely more verbose and less flexible than the Haskell version would have been. Typeclasses and higher-kinded types make an immense difference, powering everything from lens to QuickCheck? to mtl...
- Haskell's parallelism and concurrency story is incredible. OCaml's... isn't. I've used both LWT and Async in OCaml and while they get the job done most of the time (unless you're computaiton-bound), they're not nearly as powerful or convenient as Haskell's threads and high-level abstractions like STM.
- I find Haskell's libraries to be much stronger than OCaml's overall. A big part of this is that the language is expressive enough to support some really high-level, general abstractions that would be too awkward to use in OCaml.
- Controlled effects (IO, ST... etc) are a revolution for software maintainability. I would hate going back to potentially impure code everywhere.
- At the end of the day there are a couple of things I really miss from OCaml (polymorphic variants and a record system that doesn't suck). On the other hand, I missed a ton of things from Haskell back when I was doing OCaml." -- [47]
- vs Haskell: "Since someone ought to argue the other side and I'm a huge ML shill, I'll write my thoughts.
- Strict evaluation, while sometimes more cumbersome, is much easier to reason about from a performance perspective. You will never have to deal with a mysterious space leak because the code behaves predictably and the cost model is composable.
- OCaml has a far more sophisticated module system. In particular, OCaml modules actually have types, like everything else in the language! Better yet, OCaml even lets you write functions at the module level. related reading
- ML lets you encapsulate benign effects. To be fair, this is a double edged sword because the onus is on the programmer to encapsulate effects appropriately, but on the other hand if you need to use (e.g.) memoization you can do so without forcing the client code to pay attention to that implementation detail.
- Exceptional control flow is a pleasure to use in ML.
- Polymorphic variants are killer for expressibility.
- Concessions:
- The ecosystem is not as developed.
- The community is less active.
- Type-classes are really nice, and OCaml doesn't have them. (Although some fine day we won't have to sacrifice modularity to use type classes)" -- [48]
- vs Haskell: "OCaml has its good sides, too. To add another one: it is (or at least feels like) a much smaller and simpler language than Haskell. The compiler is also simpler and easier to understand, if you want to start hacking on it. Recently, I'm especially impressed by BuckleScript? that translates OCaml into readable and relatively idiomatic JavaScript?."
- vs Haskell: "What do you value most: the ability to reason about correctness, from looking at the code, or the ability to reason about run-time characteristics (speed, memory usage)? In case of the former, I would recommend Haskell, and for the latter OCaml. Purity makes it much easier to reason about correctness, from looking at the code alone, since nothing is hidden from view. Laziness, however, makes it difficult to figure out how a piece of code will perform at run-time, without actually running it." -- [49]
- vs Haskell: "Are you working primarily on Windows? The Haskell tools (compiler, package system) work well on Windows, while OCaml not so much. On Mac/Linux though, OCaml tooling works great--but so does Haskell. From the language perspective, with Haskell you get enforced purity at the cost of having to deal with effect management like e.g. using a monad transformer stack. In OCaml you don't get the safety net of enforced purity but on the other hand you can just do side effects--I/O, mutation, etc.--wherever you want. Oh, and possibly the biggest difference--Haskell is lazily evaluated so you need to keep an eye on memory usage in large computations, but in return you get the performance benefits of lazy semantics--e.g. take 5 [1..] quickly gives you the first five numbers without trying to calculate an infinite list starting from 1." -- [50]
- vs Haskell: "I am using both languages. Syntax-wise I definitely prefer Haskell, it feels so much lighter---but I admit that OCaml has fewer syntax elements to learn."
- vs Haskell: "For example, I could install a package where the latest commit was 5-8 years ago and it worked straight out of the box. That’s something unimaginable with Haskell."
- "I fell in love with OCaml. It’s a simple, very explicit language that is pleasant to work with, has strong, sophisticated type system that actually guides the development process not trying to produce scary, intimidating error messages. The type-level language has different syntax from the main language making code easier to “brain-parse”. More importantly though, all the libraries seem to have been written with the goal of getting the job done, not to defend a CS thesis...I also was stunned by how effortless it was to write a C stub. Just throw in a C file into the source directory, declare external function signature and it’s done!...On the one hand insanely fast compiler (much faster than Go’s!) and a sophisticated type system (and more powerful module system than the other two) make it extremely pleasant to work with. On the other hand it is somewhat lacking in centralised documentation. There aren’t that many books oriented both at beginner and advanced levels. The variety of available libraries/packages also leaves much to be desired. The absence of a parallel GC may incur performance hits in some situations, although low memory footprint somewhat rectifies it." -- [51]
- https://blog.usejournal.com/systems-languages-an-experience-report-d008b2b12628
- vs. F# "Let's start with the obvious, F# is OCaml. It's OCaml backed by the world's largest and most experienced creators of programming languages. And in the areas that OCaml is great, F# is also great! Sum types, static typing, eager execution, pipelines, immutable values, all of this is really great. It actually has a much better type system, in my opinion. One thing that sticks out is that OCaml made it really cumbersome to use maps. Like, hashtables, associative arrays, whatever you call them. It seems the old version of Real World OCaml has been taken down, so I can't show you how unpleasant they were at the start. Now, in the latest version, they are more moderately unpleasant to use. Whereas in F#, you have a Map<OneType?,AnotherType?> and that's really it. Magic! Of course, the main reason I chose .NET was the libraries. It has libraries for everything, what a surprise." -- [52]
- vs. F# "So...I think a decent choice to make here is to switch from OCaml to F#. You'll get almost all of the benefits and most of the drawbacks go away. And for the most part, you can directly translate the code from OCaml to F#." -- darksaints
- vs. F# "We have considered OCaml but went for F# instead. I could not be happier. Great libraries, good tooling, in 2020 F# is a first class citizen in the cloud thanks to C# and .NET Core. You can develop on Linux, Windows, MacOS? and without modification it works. Compilation times are great. Unless you want to deal with the low level nature and the C++ influence in Rust F# is a much more logical step to move from OCaml. There is dotnet fsi for REPL too. F# has access to C# libraries and it is relatively easy to write a thin wrapper that convert nulls to Nones, so you are null safe or C# style functions to ML style functions so you can use
- "While OCaml is a personal "top tier" language, I'd always prefer Haskell, Rust or Scala. Type classes / traits just seem to beat a higher-order module system for me. " [53]
- " I learned SML/NJ and OCaml around the same time and for some reason I found SML more pleasant. I particularly liked the SML "Basis" library, it felt really well designed to me. Still, I'd pick either over Scala. Superior compilation times, cleaner syntax, less complex. Haskell also just feels excessively clever. I had hoped Rust would be "OCaml but for systems programming" and it sort of is, -- but the borrow checker and memory safety features, as neat as they are, add a lot of mental overhead. "
- "...
- OCaml vs Haskell: eager vs lazy (=> memory consumption is more predictable)
- OCaml vs Rust: OCaml has a GC (=> comfort) OCaml has tco (could not resist this ;) )
- OCaml vs Clojure: vastly superior typing system. (=> less bugs)
- OCaml vs Kotlin: no JVM needed.
- OCaml vs C++: more safety. once it compiles it will not segv.
- OCaml vs Go: vastly superior typing system. (=> less bugs)
- OCaml vs Python: vastly superior type system, way better performance. The biggest risk of doing OCaml (or Haskell or Rust) for extended periods of time is that you will be unable to hide your feeling of superiority towards (fe) a python developer. " [54]
- vs Rust: "OCaml -- this one is pretty ideal when it comes to the language machinery, but two+ build systems, two standard libraries, etc, make it not a reasonable choice in comparison"
- https://news.ycombinator.com/item?id=33749945
- vs Rust: https://hirrolot.github.io/posts/compiler-development-rust-or-ocaml.html
- vs Haskell and Rust: https://github.com/sidkshatriya/me/blob/master/007-My-Thoughts-on-OCaml-vs-Haskell-Rust-2023.md
Variants and implementations:
- OcaPic
- http://facebook.github.io/reason/
- "Amazing development tools that are decoupled from an editor...can be compiled to JS using BuckleScript?, which generates performant JS...great JS interop...the only thing I dislike about it is that I have to create lots of type definitions just to use dependencies, but usually, it is okay: we don’t have to model the whole module, just the input, and output of a specific function/class/method we use. Because Reason isn’t purely functional (you can have side-effects) — to me, Reason feels like the best way of building JS right now....Reason can compile to bytecode/native as well. Using pure OCaml/Reason means that you won’t have runtime errors if your code compiles, it can be statically built too with a small footprint and fast startup. and it builds VERY FAST. I mean, wow. FAST. FAST." [55]
- "it seems that for native OCaml/Reason development, the entry level is very high, and it can be frustrating" [56]
- http://bloomberg.github.io/bucklescript/blog/index.html (compiles to JS)
- "OCaml light is a formal semantics for a substantial subset of the Objective Caml language. It is written in Ott, and it comprises a small-step operational semantics and a syntactic, non-algorithmic type system. A type soundness theorem has been proved...does not currently support objects, modules, pattern matching guards (when), mutable records, or arrays."
- https://github.com/BuckleScript/bucklescript
- https://caml.inria.fr/pub/docs/manual-caml-light/index.html
- mlml: self-hosted compiler for a subset of OCaml
- https://github.com/stevenvar/OMicroB "An OCaml generic virtual machine for microcontrollers"
Charity
https://en.wikipedia.org/wiki/Charity_(programming_language)
F#
Features:
Tours and tutorials:
Recommended libraries and best practices:
Opinions:
- "I loved working with F# for windows development but it wasn't a viable option for most of my projects." -- [57]
- "F# sounds like it fits the bill. ADT, pattern matching, performant, great tooling (multiple amazing IDEs, a REPL, etc.), and a huge ecosystem of software packages to use with it (all of .NET). It's my favorite general purpose programming language (can be used for frontend programming, server, mobile apps, etc.)." [58]
- "F# has hit a huge sweet spot for me in productivity, strong typing, inferred typing, multi-paradigm and syntactic sugar. The tooling just gets better and better. I struggle to think of a language that's so well rounded." [59]
- https://dusted.codes/why-you-should-learn-fsharp
- "Would be nice if it was not confined to .NET ecosystem and had good native compilation story. " [60]
- "I actually quite like it. It's close enough to OCaml, has great library support, and tooling which so far has been a mix of great and terrible." -- [61]
- vs Rust: "Rust has excellent tooling, great libraries, a delightful community, etc. But after spending about a month on it, I can't say I like writing Rust. Especially, I don't like writing async code in Rust. I like a nice high level language, and that's kinda what you need when you have a project as big as Dark to build. And Rust is not that. I'll publish "Why Dark didn't choose Rust" next. Or I might call it "you'll never believe just how much a Garbage Collector does for you!", because that's the summary." -- [62]
- vs. Ocaml "Let's start with the obvious, F# is OCaml. It's OCaml backed by the world's largest and most experienced creators of programming languages. And in the areas that OCaml is great, F# is also great! Sum types, static typing, eager execution, pipelines, immutable values, all of this is really great. It actually has a much better type system, in my opinion. One thing that sticks out is that OCaml made it really cumbersome to use maps. Like, hashtables, associative arrays, whatever you call them. It seems the old version of Real World OCaml has been taken down, so I can't show you how unpleasant they were at the start. Now, in the latest version, they are more moderately unpleasant to use. Whereas in F#, you have a Map<OneType?,AnotherType?> and that's really it. Magic! Of course, the main reason I chose .NET was the libraries. It has libraries for everything, what a surprise." -- [63]
- "I've been using F# on GCP in production for 3 years now and it's fantastic and only getting better. You can leverage existing .NET libraries (for example, you get official GCP libraries from google) and if you use them enough it's easy enough to write a functional wrapper around them." -- angio
- "Not everything is amazing though. The build system is attrocious. While paket is roughly on par with esy, msbuild is 1000 times worse than dune. An incremental build in dune is like 1s for me, and 6s in .NET, even if nothing is happening. I know they have fancy incremental compilers for .NET so this puzzles me; if anyone has tips on getting really fast compilation in F#, I would appreciate them." -- [64]
- "An important thing to check was whether I could compile my code to JS. Dark's execution engine runs it the editor as well, and that's one of the core things that makes Dark special. Because of this, I want to take unaltered backend code and compile it directly to JS. This isn't like Rescript (which is OCaml compiled to JS with slightly different semantics and ecosystem, or it's equivalent in F#, Fable). Fortunately, F# code can be compiled to Wasm using Blazor. Blazor compiles the .NET runtime to WASM and runs your code in that. It barely took any code to get working either (though figuring out the right incantation was not trivial)." -- [65]
- https://lobste.rs/s/rwvlpy/driving_with_d#c_ehwdv2 (on units of measure)
- https://fsharpforfunandprofit.com/posts/fsharp-is-the-best-enterprise-language/
- https://danielbachler.de/2020/12/23/what-i-wish-i-knew-when-learning-fsharp.html
Comparisons:
- https://www.reddit.com/r/haskell/comments/3huexy/what_are_haskellers_critiques_of_f_and_ocaml/
- "...problem with all of these languages is that they are still obscure compared to everything else, with all that this entails: It's harder to find developers...F# is a language that I like a lot; in some ways it looks like a more pragmatic Haskell, or Ocaml with a cleaner syntax. I don't like the fact that it's tied to .NET, which (like Java) means it inherits all the issues from that platform. The performance on Mono looks unsatisfying." -- [66]
- " I don't see how typing F# is slower, longer or less convenient than typing Python (spoiler alert, it's not). And you also get things like actual lambdas, pattern matching, currying, real parallelism and more and more. It's only that people believe there is no need to learn anything beyond Python because it's "easy", which it's not, once you go beyond several hundred lines of code. But the myth somehow continues to persist. " [67]
- vs. Ocaml "So...I think a decent choice to make here is to switch from OCaml to F#. You'll get almost all of the benefits and most of the drawbacks go away. And for the most part, you can directly translate the code from OCaml to F#." -- darksaints
- vs. Ocaml "We have considered OCaml but went for F# instead. I could not be happier. Great libraries, good tooling, in 2020 F# is a first class citizen in the cloud thanks to C# and .NET Core. You can develop on Linux, Windows, MacOS? and without modification it works. Compilation times are great. Unless you want to deal with the low level nature and the C++ influence in Rust F# is a much more logical step to move from OCaml. There is dotnet fsi for REPL too. F# has access to C# libraries and it is relatively easy to write a thin wrapper that convert nulls to Nones, so you are null safe or C# style functions to ML style functions so you can use
- (in reply to a suggestion of Scala for "statically typed functional (but not pure) language with sum types, immutable data structures, good performance, editor/IDE support, a broad library ecosystem, multithreading, a C-like syntax (I assume, since you're using ReasonML?), and a nice compile-to-Javascript experience to run in the browser" to someone who wanted to switch from OCaml): "...F#, which has all of that but with a fast compiler and is just a superset of OCaml" JackMorgan
- "F#’s type system is noticeably less fancy (basically generics are “weaker” than in OCaml) but having the entire .NET System library is very useful for practical programming, along with many 3rd party options." ojnabieoot
- vs OCaml: "It doesn't have a real module system." cultus
- vs kotlin: "
- The tooling for Kotlin is pretty good. It was initially started by JetBrains? (so stellar IDE) and since Google made it the preferred language for Android, the situation has only improved.
- Kotlin has all the “functional” features that F# has (type inference, pattern matching, first class functions). They both lack type classes or higher kinded types.
- The developer community for Kotlin is quite a bit bigger than F#. I’m wasn’t able to find a programming language index where Kotlin didn’t outperform F# (and sometimes F# wouldn’t even show up on the index at all!).
- " -- [68]
- vs Clojure, for compilers: https://www.youtube.com/watch?v=t8usj1fN9rs
- vs Rust: https://cragwind.com/blog/posts/comparing-voxel-game-fsharp-rust/
Variants and implementations
Iverson array languages
See also [69] for APL, A+, J, K, Q, some of which are sometimes called functional or function-level languages.
Elm
Tutorials:
Opinions:
- "Elm language limitations have become a real problem for me. The type system is F#-like: too limited to define Applicative, Monad, or any abstraction or data type that has a type parameter whose kind is not *. Also no higher-rank types or existential types. You can sometimes survive without these things, but eventually you reach a point where you need them. I’ve hit that point." -- https://pchiusano.github.io/2015-04-23/unison-update7.html
- " Elm’s version of FRP is not very expressive. Signals cannot be recursive or mutually recursive; the only way to introduce past-dependence is via a left fold (the foldp function) or by sending values to a sink and have them magically appear elsewhere in your program. The oddly popular Elm architecture is just a pattern for building up your entire app’s interactivity as the arguments to a left fold over the merged input events of your app! Because the signal world is so limited, most of your logic necessarily lives elsewhere. Not necessarily a bad thing, but the result has been that I haven’t gotten much mileage out of Elm’s version of FRP. Instead I’m doing the vast majority of the work with pure code that could easily be written in just about any other functional language. There are some interesting alternatives to Elm that have arisen recently. I haven’t investigated them in detail, but there’s Halogen in PureScript?, and for GHCJS there’s Reflex and React-Haskell. There are probably others. A GHCJS option interests me since it means I could share code between client and server. I look forward to exploring this further…
The only thing that seems missing from the alternatives is something like Elm’s Element static layout library, which I need for some of the things I’m doing. But it seems like a reasonable path forward might be to just write such a library or port Elm’s to whatever tech I end up using." -- https://pchiusano.github.io/2015-04-23/unison-update7.html
- https://charukiewi.cz/posts/elm/
- https://news.ycombinator.com/item?id=22825553 (to summarize: too many breaking changes)
- "It's an awesome language, but there is basically a big case of 'vendor lock-in'. Certain compiler features are only available to the maintainers, nor is there any way to use alternative package sites." [70]
- "Many things contributed to my giving up Elm, but the final straw for me was when the core team removed some userland APIs under the guise of "we can't trust developers not to use these to write bad code"." [71]
- "I looked at Elm a while back (maybe two years ago, but I'm not sure exactly). I decided against it after I ran into some issues related to language instability and old documentation. I dug a little further and it appeared to be how the Elm project was run, so I stayed away." [72]
- https://underjord.io/my-elm-experience.html
Retrospectives:
Erlang
" Here is how I think Erlang works. I believe Akka is very similar.
Each process has a single mailbox. Messages are put into the receiver's mailbox by the sender, and fetched by the receiver using pattern matching. This matching process can change message ordering in the sense that the oldest message in a mailbox may not match, but a younger one does. In this case the younger one is consumed first. Other than that, message ordering is preserved. "-- [73]
Pros:
- Hot code reloading; can literally reload code without dropping a TCP connection
- Per-process heaps for robustness
- supervision trees for robustness
- Small concurrent workloads [74]
- low, predictable latency [75]
- Fault tolerance [76]
- runtime console introspection/tracing/profiling/debugging [77]
Cons:
- Said to be bad at efficient text processing (todo cite)
- Prolog-like syntax is disliked by some
- Moderate startup latency makes command-line scripting a little slow [78]
- CPU throughput is not great [79]
- No shared memory for parallel computing [80]
- Difficult to make single-binary executables for desktop deployment [81]
- Macros are only text-replacement macros [82]
Features:
- pattern matching
- shared-nothing message-passing concurrency
- "lack of in-process concurrency means a process never has to be written to worry about being interrupted or interfering with itself." [83]
- immutable data
- "If process A sends a message to process B and B is alive, the message is guaranteed to arrive." [84]
- "Messages pool in a per-process inbox in the order in which they were received." [85]
- "Message passing is blind to local/remote process distinctions. That is, intra-VM messaging is accomplished in the same way--from the point of view of the programmer--as inter-VM messaging." [86]
- "Processes can 'link', receiving special messages when pairs crash." [87]
- "Processes have a unique ID--called a PID--but may optionally be given a per-VM or per-cluster unique name." [88]
- "distributed, fault-tolerant, concurrent, garbage-collected programming language which supports soft real-time guarantees, code hot swapping, message passing, reactive programming, and introspection..." [89]
- https://underjord.io/unpacking-elixir-concurrency.html
Tutorials:
Overviews:
Books:
Best practices:
Respected exemplar code:
Notable distributed libraries:
Retrospectives:
Opinions:
- http://kkovacs.eu/erlang
- "concurrency...Erlang/Elixir can do it better than any other language is really all you need to know. " -- http://lebo.io/2015/06/22/the-unix-philosophy-and-elixir-as-an-alternative-to-go.html
- "Erlang (and by extension Elixir) are good languages for web development, " -- [93]
- "Erlang (and by extension Elixir) ... 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...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....I wasn't talking about more abstract distributed services, rather stuff that brutally exploits POSIX and kernel-specific APIs." -- [94] [95]
- "supervision, multi-node distribution, hot reloading" -- [96]
- http://blog.troutwine.us/2013/07/10/choose_erlang.html
- "Possibly the language which does ((concurrency)) best is Erlang, although it's not as easy to get started with as node.js. Theorists really overlook the vital metric of "time to hello world" in language learning" [97]
- "Things I like about " the BEAM (the Erlang VM):
- Process isolation : sounds like magic but it's true you can have millions of processes _and_ all having isolated heaps.
- Dynamic tracing : connect to a node and start tracing any function
- Hot code loading : don't feel like stopping a critical production server to add an extra log statement or upgrade it, don't have to. Can just load the code.
- Like you mentioned, GC. It will never "stop the world". When process ran and exited, the whole block can be reclaimed quickly and efficiently." -- [98]
- " In order to implement next generation Universal
Intelligent Systems [Hewitt 2019], the following extensions
to Erlang are needed:
• Automatic reclamation of processes. For example,
Erlang uses Process Identifiers (PIDs) to communicate between
processes. However, a process that has a process identifier
(say ProcessIdentifier?) of a process with which it
communicates can launch a denial of service attack to kill
that process using exit(ProcessIdentifier?, kill). An Erlang
process can be orphaned if its Process Identifier (PID)
becomes inaccessible. By default, the send operation in
Erlang always succeeds (even if the target is a non-existing process).
• Strong parameterized types with no type Any.
• Region of mutual exclusion for an Erlang process.
An Erlang process does not have a region of mutual exclusion,
which can lead to internal conflicts within processes.
• Behavior change using variables in a region of mutual
exclusion instead of having to create auxiliary processes to
hold state with callbacks. [Swain 2014] In particular,
o Better support for holes in the region of mutual exclusion
of an Actor implementation. For example, in Erlang it is
necessary for application programmers to explicitly code a
request handler for each re-entry into the region of mutual
exclusion potentially enabling cyberattacks from outside the
process.
o Better ways to upgrade Actors in place without losing work in progress. " Carl Hewitt
Comparision opinions:
- "I also used Erlang and in my experience the JVM can achieve much lower latency and better throughput. Where Erlang systems tend to shine is in being more resilient and adaptable." -- https://news.ycombinator.com/item?id=9763236
- "for the problem space the author mentions here, web services, Elixir is clearly superior." -- [101]
- Erlang vs. Go
- "The key difference to an Erlang'er is that when something goes wrong you can terminate the process and restart it sanely (which others might argue is why you used an architecture with a channels design in the first place to have an arms length relationship between the code...). With Go all bets are off, you can have a pool of processes watching a channel in case one gets stuck, but even then all processes could get stuck or one can die and take down all the others (shared state...), there is no reliable way to deal with failure on the other end of the channel. " -- https://groups.google.com/d/msg/elixir-lang-talk/Tr8ayRHMOh4/FJOynFniXW4J
- " I'm not sure in what setting would Erlang be inappropriate, but somehow Elixir would be fine. Elixir actually adds a further layer of complication compared to Erlang, which is a straightforward pattern matching, expression-based language with recursion and polymorphic functions for looping and conditionals. Elixir looks Ruby-ish, but that illusion breaks down very quickly since the underlying semantics are anything but. Not to mention Elixir doesn't shield you from having to be aware of the Erlang runtime, the VM and OTP. " -- [102]
- "OTP being missing matters less than you might initially think, too. I implement what bits of supervisor trees can be implemented here: https://github.com/thejerf/suture , and in general, with the way Go is structured, gen_servers and such just sort of melt away into "things you interact with over channels" without much fuss. (IMHO, OTP is generally a good thing, but the reification of gen_X so deeply into the language was an error. I actually prefer Go+Suture's way of bringing up a server; much simpler, virtually no loss of capability in practice. Again, the theoretical difference turns out to be larger than the practical difference.)" -- https://news.ycombinator.com/item?id=10153024
- "...Erlang/OTP (and therefore also Elixir) is attractive because it's already a complete package...For example, you can build an approximation of supervisor trees in Go, but since goroutines cannot be killed, you're at the mercy of the runtime. (I also wonder if goroutines scale as well as Erlang's.) Similarly, no one has actually built a functional, immutable, process-oriented call-by-name-and-signature RPC mechanism for Go, so you'll have to invent your own....I was recently investigating OCaml to see if someone had implemented something similar to OTP. I found a couple of promising, nascent projects, but their activity all ended around 2009. Apparently nobody is doing it, even though the language seems damn near perfect for it. Never mind distributed processes; LWT is pretty weak even as an approximation of Go's goroutines...Erlang's live code replacement changes how you mentally think about versioning, I think. Instead of designing every piece of your infrastructure to transparently transition to a new version — using load balancers, multiple processes accept()ing on the same port, and so forth — you get a first-class language construct. This also opens possibilities beyond code deploys, such as temporarily injecting tracing into a running program. Also not often mentioned is how powerful it is to be able to connect to a running process and run the REPL inside it. (Ruby comes close here, but it's never been something you get for free.)" [103]
- Q: "Why would one choose erlang/elixir over akka[0]? Akka seems to implement a lot of common patterns for you. It has actor persistance, cross-node failover, advanced mailbox and routing logic, and is basically a superset of OTP. The JVM is generally more efficient, has better tooling (ide support), has better libraries, and so on. The only point I can give to erlang is the pre-emptive scheduling, which is useful for consistently low latency." A1: "I use both Akka and Erlang/BEAM is heavy production. There is a huge difference in multiple aspects. 1. GC and context switching internals. This becomes obvious quickly under load. While you can tune Akka in some ways and Akka will generally kick Erlang's ass in terms of performance, the response times and the number of usable concurrent entities are in favor of Erlang. 2. Libraries - what libraries are you going to use in Akka? JDBC/JPA? java.net? HTTP clients to pass REST requests around to modern integration endpoints? Most if not all are blocking and will bring Akka to it's knees when something freezes. Erlang libs are either non-blocking or blocking doesn't matter because it will handle millions of processes with little overhead. 3. Production ops - deploying and rolling back code in Erlang has tons of gotchas, but you can mostly change code and DB schemas on the fly in production, selectively or even run parts of the production load on your laptop to debug it. And you can deploy changes in small increments many times a day without affecting uptime. 4. Best part is failure recovery. In Akka, the node is a single OS process and single JVM. If you leak a file handle or some resource or lock and a thread blocks nothing will recover you until your OS process dies. Erlang is capable of killing processes everywhere, there is no internal global state. Even if you leak something like a file handle in a process, it will be cleaned up when the process terminates like a real OS process. Even if you leak something in ets/mnesia(the global storage), you can go ahead and clean it up from your erlang shell and production doesn't have to stop. If you leak something in Java and you haven't exposed specific interface to clean it, you will have to kill the JVM. " A2: "1. BEAM processes are lightweight which means smaller memory footprint and cheaper context switching between processes. 2. As others have mentioned, garbage collection. Processes don't share memory, so data passed between processes are copied. Virding says that this is a tradeoff, but worth the benefits of keeping garbage collection simple and fast..." A3: "Per-actor GC is pretty important, as it allows Erlang to guarantee soft real-time / low latencies. This is simply impossible in the JVM unless you go with Azul's GC, which then sacrifices throughput....Thanks. Another point is that in Erlang/Elixir, if you have many actors, you will have less garbage per actor. Which means faster GC overall." [104]
- "Erlang is the culmination of twenty-five years of correct design decisions in the language and platform. Whenever I've wondered about how something in Erlang works, I have never been disappointed in the answer. I almost always leave with the impression that the designers did the “right thing.” I suppose this is in contrast to Java, which does the pedantic thing, Perl, which does the kludgy thing, Ruby, which has two independent implementations of the wrong thing, and C, which doesn't do anything." [105]
- "It is certainly not a language for beginners. The syntax is strange to programmers hailing from the C diaspora. Functional programming is tough, and Erlang doesn't put any sugar on the pill. The graphics toolkits are primitive..." [106]
- "Erlang is also lacking in libraries compared to other languages; in my experience, for any given task, there is zero, one, or at most two Erlang libraries available for the job." [107]
- "...the only truly bad news about Erlang is that it is slow. For the server applications I have written, the speed of the language has not been an issue; the extra cost in CPU was more than made up by Erlang's correct handling of garbage collection, network I/O, and string concatenation in a concurrent environment. In the language of complexity analysis, Erlang programs tend to have a large constant out front but excellent asymptotic properties." [108]
- "While Erlang/Elixir is very good for things like concurrency, fault-tolerance, scalability, parallelism, etc it is not good for things like raw numerical calculations." -- Robert Virding, co-inventor of Erlang, https://elixirforum.com/t/is-elixir-a-good-first-language/6366/6
- https://www.evanmiller.org/why-i-program-in-erlang.html
- "Some Erlang DevX? limitations
Types
[109]:
- int
- float
- atom
- bit-string (and "Bit strings that consist of a number of bits that are evenly divisible by eight, are called binaries")
- reference
- function
- port identifier
- pid (process identifier)
- tuple
- map
- list
- string
- record
- boolean
[110]:
- any %% The top type, the set of all Erlang terms
- none %% The bottom type, contains no terms
- pid
- port
- reference
- [] %% nil
- Atom
- Bitstring
- float
- Fun
- Integer
- List
- Map
- Tuple
- Union
- UserDefined?
Erlang: Examples
A message queue:
-module(messagequeue).
-export([new/0]).
-define(TIMEOUT, 300000).
new() ->
queue([]).
queue([]) ->
receive
{get, PidReturn} ->
returnnextadd(PidReturn);
{add, Message} ->
queue([Message]);
message_received ->
queue([]);
empty_queue ->
exit(self())
after ?TIMEOUT ->
exit(self())
end;
queue(Messages) ->
receive
{get, PidReturn} ->
[Next | _] = Messages,
PidReturn ! {ok, Next},
queue(Messages);
{add, Message} ->
queue(Messages ++ [Message]);
message_received ->
[_ | Remaining] = Messages,
queue(Remaining);
empty_queue ->
exit(self())
after ?TIMEOUT ->
exit(self())
end.
returnnextadd(Pid) ->
receive
{get, PidReturn} ->
Pid ! {error, new_client},
returnnextadd(PidReturn);
{add, Message} ->
Pid ! {ok, Message},
queue([Message]);
message_received ->
queue([]);
empty_queue ->
Pid ! {error, empty_queue},
exit(self())
after ?TIMEOUT ->
exit(self())
end.
-- from [111] (Apache License, Version 2.0)
(syntax: "sending a message is "address ! message"" [112])
Erlang Internals
"An Erlang process is lightweight compared to operating systems threads and processes. A newly spawned Erlang process uses 309 words of memory...The size includes 233 words for the heap area (which includes the stack). The garbage collector will increase the heap as needed." [113]
Erlang is the culmination of twenty-five years of correct design decisions in the language and platform. ...
Take garbage collection. When it's time to collect garbage in other languages, the entire system has to stop while the garbage collector runs. This approach is perfectly fine if your computer program is supposed to run once, write some output, and then quit. But in long-running applications, such as desktop, mobile, or server programs, this strategy results in occasionally frozen UIs and slow response times. Erlang programs, on the other hand, can have thousands of independent heaps which are garbage-collected separately; in this way, the performance penalty of garbage collection is spread out over time, and so a long-running application will not mysteriously stop responding from time to time while the garbage collector runs. ... Or take string concatenation. If you pop open the implementation of string concatenation in Perl, Ruby, or JavaScript?, you are certain to find an if statement, a realloc, and a memcpy. That is, when you concatenate two strings, the first string is grown to make room for the second, and then the second is copied into the first. This approach has worked for decades and is the “obvious” thing to do. Erlang's approach is non-obvious, and, I believe, correct. In the usual case, Erlang does not use a contiguous chunk of memory to represent a sequence of bytes. Instead, it something called an “I/O list” — a nested list of non-contiguous chunks of memory. The result is that concatenating two strings (I/O lists) takes O(1) time in Erlang, compared O(N) time in other languages. This is why template rendering in Ruby, Python, etc. is slow, but very fast in Erlang. " [114]
Erlang implementations
- Official implementation (BEAM)
- https://github.com/lumen/lumen#about
- AtomVM "AtomVM? implements from scratch a minimal Erlang VM that supports a subset of ErlangVM? features and that is able to run unmodified BEAM binaries on really small systems like MCUs."
- "Currently, AtomVM? implements a strict subset of the BEAM instruction set, as of Erlang/OTP R20. Previous and later versions of Erlang/OTP are not supported. The currently unsupported Erlang/OTP and BEAM features includes (but is not limited to): Anonymous Functions Maps Nummerous BIFs and NIFs supported by the R20 BEAM Numerous ports and drivers supported by the R20 BEAM The Erlang/OTP standard libraries (kernel, stdlib, sasl, etc) epmd and the disterl protocol "
Erlang variants
- Elixir (see Elixir section)
- See also list of languages which target Erlang's BEAM VM: https://github.com/llaisdy/beam_languages (this document also has a list of links that give information on how to write a language targeting BEAM)
Links:
Elixir
Targets the Erlang VM (BEAM).
Elixir Tutorials and books
And Elixir frameworks:
Elixir Anti-features
- "no operator overloading" [116]
Elixir Opinions
- "What I do want to touch on are the qualities of Elixir itself. Do you want a modern language? Elixir is functional, immutable, and supports pattern matching, which is like a case or if statement on steroids, only that explanation doesn't begin to touch how much it impacts your entire manner of coding. It also supports macros which means that the core language can remain small but users can extend the syntax to support patterns the designer never dreamed of. As I said before, it does this with a Ruby-ish syntax. Syntax shouldn't matter but it really does. I've tried designing an "acceptable lisp" (or rather an acceptable syntax for myself) before with prose (I think it is really cool!), but with its small core and functional nature Elixir strikes me as the unintentional true heir to Scheme's throne." -- http://lebo.io/2015/06/22/the-unix-philosophy-and-elixir-as-an-alternative-to-go.html
- "Elixir with frameworks like Phoenix and libraries like Ecto seems like a much better fit as a tool for web development than many other options...a stronger choice than Go" -- http://lebo.io/2015/06/22/the-unix-philosophy-and-elixir-as-an-alternative-to-go.html
- "NO DOWNTIME code upgrades...incredible request responsiveness as well as reliability..Immutable data...Extreme concurrency..."Actor model"...awesome community?" [117]
- "I agree with the statement that Elixir is awesome...Elixir might be dangerous as well...It is essentially a big pile of macros, so sometimes number of keywords that you have to keep in your head is staggering.... Elixir sits above golang but way below Java or C++. Maintainers are doing pretty good work at simplifying things as language matures - for example merging Dicts and Maps together....Also, Elixir is a pretty big language...My concern is not about the macros themselves (I don't want to start war on macros and DSLs here) but the amount of them - especially in phoenix. Hard to grasps at first - later it is a huge benefit." -- [118]
- "> Elixir is a pretty big language...Compared to? Perspective matters a lot. It is definitely bigger than Erlang but I would still classify it as considerably smaller than languages like Scala and C++ and still smaller than Ruby and Python....Compared to? Perspective matters a lot. It is definitely bigger than Erlang but I would still classify it as considerably smaller than languages like Scala and C++ and still smaller than Ruby and Python...Compared to? Perspective matters a lot. It is definitely bigger than Erlang but I would still classify it as considerably smaller than languages like Scala and C++ and still smaller than Ruby and Python....Compared to? Perspective matters a lot. It is definitely bigger than Erlang but I would still classify it as considerably smaller than languages like Scala and C++ and still smaller than Ruby and Python....> the barrier and number of concepts that you have to get familiar with is huge...I definitely agree with this. There are functional aspects, like pattern matching and immutability, the concurrency aspects and the OTP bits. There is definitely a learning curve although most patterns, as you said, are necessary for building the kind of systems Erlang/Elixir enable you to."
- "We're busy rewriting a part of our Rails app that we are having difficulty scaling in Elixir. So far what I've seen is phenomenal. Coming from Ruby/OO, it's a bit weird sometimes, but the code is very readable and it's insanely fast. Really the first language since Ruby that I'm quite happily riding the hypetrain for." [119]
- "I have to say that learning and using Elixir has been more pleasant than any other language I have ever worked with. Code is easy to follow due to the functional nature of the language, and the community is one of the best out there. The applications I've written and deployed have been more stable and fault-tolerant than anything else I've ever done, and I've yet to have any performance issues with the language in production." [120]
- "I used to be/still am a ruby on rails developer. I wrote a very naive web crawler in elixir within a week while learning elixir and I'm blown away by the performance." [121]
- "The transition to a functional language is not exactly a cakewalk and I honestly still miss ruby's ability to be able to hack through arrays (no way to easily get the nth element of an array, it's all head
tail recursion) and hashes (nothing like hash) to make something "work" but the pipe operators and pattern matching are addictive." [122] |
"Not that you should do it too much since traversing arrays (lists) is expensive, but http://elixir-lang.org/docs/stable/elixir/Enum.html#at/3 gives you the ability to grab arbitrarily." [123]
- "What I wanted to highlight is the usefulness of the pipe operator
>. It works just like the unix pipeline and shuffles the return value of function A into the argument of function B. Instead of writing b(a()) you can write a() | > b(). There are many more things I like, for example pattern matching, the built in Stream module, extensibility via macros and protocols, documentation in markdown format, shared nothing concurrent programming (actor model) and much more." [124] |
- "The ease of navigating an Elixir project is a huge benefit. If I want to know where a function is defined, it's very easy to find it. There's none of this class-reopening or meta programming malarkey that you get in Ruby (where is a Rails controller's render method defined exactly?)... it's all right there in the module. Another thing that I love is that it's trivial to separate processing of several pieces of data into separate Elixir processes. To do something similar in Ruby, I would probably need to either use concurrent-ruby or spin them off into a background job with Sidekiq. In Elixir, it's baked right in." [125]
- "I would view Elixir as an improvement (iteration?) of Ruby built on Erlang. The article points out it was written by Jose Valim . It derives its structure from the documented shortcomings of both parent languages." [126]
- "There's a lot of niceties you get out of the box (mix is pretty nice and the testing framework is included, the REPL is good, love that they improve the error messages and consider an unclear error message a bug). I'm already used to pattern matching from Prolog so that's a great plus (I wanted to use Erlang initially but Elixir has won me over). I also feel like I'm thinking in a functional way much clearer..." [127]
- "Another one that wasn't mentioned: optional types. Sometimes you don't need / want em, but it's good to have the option available for when you do. Aside from that... Elixir feels really fun. " [128]
- "If you use Dialyzer you get compile time type checking of for most operators too" [129]
- "What Erlang is not really suited for is where you need multiple levels of abstraction, such as when implementing complex business logic. You would think that the functional nature of the language lends itself to that, but then you quickly realize that because the primary concern of an Erlang engineer is to keep the system alive, and for that reason you must be able to reason and follow the code as it is running on the system, all kinds of abstractions are very much discouraged and considered bad practice (look up "parameterized modules" for an example of a feature that was _almost_ added to the language but was discarded in the end)." (note: although this post mentions a disadvantage, seen in the context of the rest of the post, this quote is actually very pro-Erlang) [130]
- "It's fine. Parts of it seem really weird to me. Agents don't seem like they should be in the standard library. The pipe operator should probably have some sugar for representing the first implicit argument to the function because it's weird to have things like Enum.map/2 become written as Enum.map/1 in that context. Protocols are bizarrely complex (you can do the same thing in Erlang with about 10% of the ceremony) and made weirder by the fact that structs are represented as maps with a special key instead of as records, so field access and key walking has an unnecessary amount of overhead comparatively. It doesn't feel "complete", because there are pretty big chunks of the underlying system that still make you extremely aware you're really running atop Erlang (debugging, tracing, profiling, etc.), up to and including the docs themselves which often just point you straight to the relevant Erlang documentation. I like that it has brought a lot of new energy to the BEAM ecosystem. I like the tooling and focus on docs and testing that have come with it. I like the macro system, even though I think it's overused by the community and often to ill effect, but the system itself is much cleaner than Erlang's. tl;dr... Sure, why not. :-) " [131]
- "...Elixir (with its guards and pattern-matching facilities) occupies a nice middle ground between strict static typing and the dynamic typing of procedural/OO langs." []
- http://digitalfreepen.com/2017/08/16/elixir-in-depth-notes.html
- https://news.ycombinator.com/item?id=27192873
- "The first thing that tripped me up with Elixir is the difference between single quoted and double quoted strings. They’re different data types!" [132]
- "What I really noticed last week when I did a day of Python after a week of Elixir is how much time I spend in other languages writing branch logic code. Code where you decide what direction your code should go next. Should it do this or that now. Or if it should optionally do that. I'm writing an order of magnitude less of that code (it moves to a more orthogonal place in multi function matching) in Elixir." [133]
- "when I dove into the language I realized it addressed virtually every short term and long term problem that I’ve experienced in my programming career. The rules it sets out force things to be done in such a way that many issues are avoided as a by product. I find it balances all of my concerns better than anything else (productivity, performance, maintainability, scalability, learning curve, concurrency, stack simplification, capability, refactorability). " [134]
- "While at NoRedInk?, I had picked up some Elixir, and during university I had looked into Erlang. We used it at Omni for some of our critical infrastructure, and until last week when they started migrating it to a different provider, it had no errors. So I was a big fan of Elixir. But having used it for a serious amount of time, I’m not such a big fan. The story with types is not great. A lot of documentation is missing. It’s possible to write code that says it is typesafe, but it is not at all. Instead of relying on types to handle awkward situations, Elixir encourages you to just have errors. I don’t think I would use Elixir again on any new projects, though I do appreciate the step up over Ruby." -- [135]
- https://joearms.github.io/published/2013-05-31-a-week-with-elixir.html
- https://discuss.ocaml.org/t/reflections-on-my-first-completed-application-in-ocaml/6768 (a lot of ecosystem comments here; some resolved since, according to [136])
- https://paraxial.io/blog/elixir-savings
Elixir Comparisons
- "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." -- https://news.ycombinator.com/item?id=9762003
- Elixir vs. Go
- Elixir "can do a few things a bit better: Streams, Actual macros, Mutable variables (not data, of course). Those would be kind of a pain to do in Erlang, maybe with parse transforms. I personally prefer Erlang better. I like its syntax and immutable variables. Haven't hit a need to need macros too badly yet." [137]
- vs Scala: "I used to do Scala prior to coming to Elixir, mainly by using Play Framework to create web apps. I can tell you that I love Elixir and that I will not be going back to Scala. I miss nothing from Scala. * Pattern matching in Elixir is superior to Scala's in every way possible (You can pattern match on function parameters!!) * Async code is a breeze with Task.async()/Task.await() instead of mapping on Future's everywhere and making sure you have the proper implicit globals imported * Actor pattern is built into the language and it's a breeze to work with. No need to rely on Akka to replicate it * JSON is a joy to work with * Documentation is fantastic in the core language and many libraries * No more JVM! * Building a release is much easier than dealing with SBT" [138]
- "You can use https://github.com/scala/async for async/await in Scala. It's super easy. Also, you can pattern match function parameters in Scal((a)) using PartialFunction? `case` syntax." [139]
- on the fact that Elixir is dynamically typed: "There are optional type specs [140] which can serve as documentation and are used by a tool called Dialyzer during the build process to find errors based on the type specs....The Elixir compiler seems to catch a lot of errors, for what it's worth. If you attempt to call a function that doesn't exist or with the wrong arity, you're going to get an error (even w/o any type specs). "
- "the collections library (this is big for me -- I could never go back to a weak collections library -- Go, I'm looking at you), " [141]
- "elixir's compiler is pretty good for supporting a big rename, for example; ruby, without a compiler and with so many language features, not so much" [142]
- vs Clojure: "Elixir, though very small, still seems to be moving forward, and perhaps has a chance to answer some of the demands that people made of Clojure (more pure? easier syntax?)." [143]
- vs. Clojure: "I really think the impedence mismatch between Clojure and the underlying Java implementation is a big, painful problem...Elixir/Erlang don't suffer from this problem, it's FP all the way down." [144]
- vs Clojure: "I think the Elixir team visibly care about ergonomics and good design, much more than the Clojure team seem to. Look at `mix` and compare it to `leiningen`, honestly. Look at Plug and Phoenix and compare to something like Luminus in Clojure-land. The difference is night-and-day. Look at how fast the `iex` repl boots up, or how fast the stop-compile-start workflow is in a Phoenix app. Even look at the content of the languages respective homepages (http://elixir-lang.org and http://clojure.org)" [145]
- vs Clojure for parallel programming: "Clojure worked for me until I started to do parallel programming. I tried to use core.async but immediately found that I'm using two distinct style of programming, and code reuse became a serious problem...this eventually lead me to drop Clojure and Clojurescript, and use Elixir and Elm instead." [146]; [147] agrees
- https://blog.codeship.com/comparing-elixir-go/
- "...quickly spiking out things, with quick feedback. Elixir has a similar experience, but less of the warts you get with Ruby (like monkeypatching)." [148]
- vs Ruby: "I’m mostly an Elixir dev, but since the Erlang Runtime System sucks at POSIX (bad at pipelines, bad at exit codes, bad at trapping signals, etc.) I personally switch to Ruby when I need a glue language." [149]
- vs. Ruby "I found that I was coding almost exclusively in a functional style, in Ruby (because it was easier code to test and it seemed to produce less bugs/fewer mental-model problems), then realized I was still missing out on the guarantees that Elixir gives you, then I thought, what's the point in staying when I could get guarantees that eliminated entire classes of bugs for free? Having done Elixir for a few years now, I RARELY have to go into "debugging mode", I've never even had to fire up Elixir's version of "pry", and my tests have caught all but a few bugs. All my logic lives in sensible locations. It's pretty nice! At least for web dev...That, plus Elixir was literally 5 to 10x faster at a sampling of tasks I threw at both (but this was a few years ago)" [150]
- vs. Ruby "I had the same epiphany; the hard-won "best practices" I had discovered in Ruby/Rails ended up looking a lot like second-rate functional programming. Having spent the last year with Elixir in production, I don't plan to go back. Things like Ecto, ExUnit?, error handling using "with", Phoenix Presence, domain modeling using Phoenix Contexts, Absinthe for GraphQL? have made programming a joy. " [151]
- "Having done python for many years and coming to Elixir the things that I find Elixir does really well are:
- mix and the general tooling (package management, testing, deployment)
- iex - specifically being able to run a shell at the same time as running my app. It makes it easy to test and explore code from the REPL
- documentation (both in iex and hexdocs) - Navigating Elixir documentation is standardized and easy
- Immutability - it took a while to get used to but now I wouldn't have it any other way " [152]
- vs Golang: "...after almost 4 years of working with Elixir and watching Go from arms’ length, I literally see NONE of the criticisms regularly leveled at Go" [153]
- vs. Go, Zig, Rust, Perl, Python, Ruby: some commenters on https://elixirforum.com/t/is-elixir-adequate-to-build-a-cli-tool/1875/11 and https://elixirforum.com/t/how-suitable-is-elixir-for-a-cli-application/36184 suggest that Elixir is not the best choice for a commandline (cli) tool/utility/application, because it requires the VM to either be installed or bundled (and bundling would make a large executable, as well as require you to "know a considerable amount about your target environment"); and because it has roughly 200ms startup time; and because by default it starts as many processes as there are CPU cores
- Would you still pick Elixir in 2021?
- "One of the reasons I enjoy Elm is its radical simplicity (and, relatedly, its stability). I’ve been using Elixir more recently, and there’s a clear overhead to learning it and using it in comparison. Some of its complexity seems particularly unnecessary - eg there shouldn’t really be special syntax for calling anonymous functions, it’s not great to have different ways to access fields in structs vs maps, and so on...Elixir still takes a commendably conservative approach to features in comparison to other languages like Rust, TypeScript? and C++." -- alexkorban
Elixir BEAM vs JVM opinionated comparisons
- "I find failure to be more fatal on the JVM. That's neither a bug nor a feature, it's just what it is. For example, BEAM puts the process call stack on the heap, so you don't need to tune them separately. You won't hit a stack overflow, you'll just run out of memory. Many, many characteristics like that exist. I prefer a working slow system over a broken system, but I fully understand the preference for loud failures." [154]
- "...the memory usage the JVM...BEAM has many great advantages such as fault tolerance and multi-node distribution built in. This allows concurrency to be a first-class citizen for applications running on the BEAM. I'm not saying that concurrency can't be done on the JVM, but that I've found it to be much simpler using BEAM. On the other hand, the JVM excels at computational work compared to the BEAM. Both VMs have their place and can be better at different types of problems comparatively. " [155]
- "...1) gc is per-process, not global/stop-the-world 2) the VM is responsible for scheduling processes using threads, and can pre-empt...blocking the scheduler is considered a VM bug." [156]
- "a runtime with preemptive light-weight processes from the ground-up (instead of tackled on top as a library/framework) with per process garbage collection and inter-node communication taken care of" [157]
Elixir Notes
Elixir Gotchas
" This is more for people looking at erlang/elixir than a critique of the blogpost or a suggestion for a change.
> Within Elixir, there is no operator overloading, which can seem confusing at first if you want to use a + to concatenate two strings. In Elixir you would use <> instead.
When this popped up, it reminded me of something people try to do often and then have issues with performance. You probably do not want to concatenate strings.
"Yes I do" you'll first think, but actually erlang has a neat commonly used thing to help here.
Let's say you're doing some templating on a web-page. You want to return "Welcome back username!". First pass (a while since I wrote erlang so forgive syntax errors):
welcome(Username) ->
"Welcome back " ++ Username ++ "!"
Now it's going to have to construct each string, then create a new string with all three. More creation & copying means things get slower.
Instead, many of the functions you'd use to write files or return things over a connection will let you pass in a list of strings instead.
welcome(Username) ->
["Welcome back ", Username, "!"]
Now it's not copying things, which is good. But then we want to put the welcome message into another block with their unread messages.
full_greeting(Username) ->
welcome(Username) ++ unread_messages()
More appending than is good here, concatenating lists is going to take time. Of course, we could put it all in one function, but then we lose re-usability in the templates and have horrible massive functions. While this is a simple example, I hope you can picture a larger case where you'd want to split up the various sections.
Anyway, there's a better way of doing this. The functions that take lists of strings actually take lists of strings or other lists. So we can just do this:
full_greeting(Username) ->
[welcome(Username), unread_messages()]
You can keep going, nesting this as much as you want. This saves a lot of copying, allows you to split things up and avoids having to keep flattening a structure.
So, for people about to get started, try not to concatenate your strings, you can probably save yourself and your computer some time.
For more info on this, you want to search for "IO Lists" or "Deep IO Lists". " -- IanCal?, [158]
Elixir libraries and best practices
Alice
https://en.wikipedia.org/wiki/Alice_%28programming_language%29
?also said to have some constraint programming?
Oz
https://en.wikipedia.org/wiki/Oz_%28programming_language%29
?also said to have some some logic programming?
Tutorials:
Oz Kernel language: http://mozart2.org/mozart-v1/doc-1.4.0/tutorial/node1.html#chapter.introduction section 1.2
Other functional toy languages
POPLMark's FSub
Supervision trees
" In Erlang, if a process faults and it has a supervisor, the supervisor gets a message that one of its children died with some additional metadata, it can then log the issue, restart a child, … This also led to an interesting "let it crash" philosophy (since Erlang processes have isolated private heaps a process dying also cleans up any potential data corruption in the process's private state, this is often considered a feature, but at its core it mostly avoids processes dying unnoticed.
Incidentally, supervision is actually the result of two individual features: linking and trapping. Linking means that when one process dies with an abnormal status (any reason other than regular process exit), all process linked to it will also die (the EXIT signal gets propagated through the link), which is repeated until all linked processes are killed, trapping lets a process handle the incoming exit signal in code rather than automatically suicide
Finally, because supervisors and supervision trees are so common the Erlang standard library provides generic behaviours which let developers declaratively set up which strategies they want in their supervisor, the maximum restart frequency (beyond which the supervisor consider the system's broken and shuts down), ... " [159]