Would be interesting to get Lobsters' take on this. Thanks!
I think it might be visually clearer to weight these arrows by how likely they are, or some such. Even if I agree with the reasoning of the first two choices, presenting them that way makes it look like 75% of projects need to be in C, whereas IME embedded or realtime projects are a tiny minority.
The top part of the chart is outside my expertise, but there are other options on phone platforms, and for large codebases on the web, js_of_ocaml, ScalaJS, Elm etc. are options. For embedded projects I might consider Rust, and for realtime projects Rust or Erlang.
“Care about GC pauses” phrases the question wrongly. If you have a hard latency requirement then C++ may make sense, but the cost in terms of development time and defect rate is very high (and again, Rust or Erlang might be better options in that case, though it’s not my area of expertise).
“Need to integrate with…” could be a question for any platform, and is probably the first question of all. (Though on the JVM I’d use Scala rather than Java, and similarly on the CLR I’d likely use F#).
Rather than “what is your constraint?”, I’d make that “do you have a performance constraint that’s worth paying a substantial cost in development time for?” And I’d probably subdivide that, because there’s a big difference between being 2x slower than (hand-tuned) C (Java, OCaml, …) and being 50x slower than C (Python, Ruby, …) - so maybe the options are “no performance constraint”, “within a factor of 2”, and “absolute maximum performance”. On the “no performance constraint” path I’d want a “large codebase?” decision, with a typed language on the yes branch (Scala for me, though OCaml, Haskell or maybe F# are defensible choices - maybe I’d add another question of “is this an interactive tool that needs to start quickly”, in which case OCaml, since while Scala’s throughput is great its startup latency can be poor) and Python or Ruby on the no. “Within a factor of 2” would go to the same place as the “large codebase” options (i.e. Scala for me). For “absolute maximum performance” I’d probably go with Fortran.
 Edited to add: actually, probably a “large codebase or low defect rate” decision - there’s an equivalence in that the larger the codebase the lower the defect rates for individual components have to be. The correct measure is probably something like required defect rate / complexity of requirements^2, because a large codebase not only means more defects, but also means the impact of each defect is larger. And potentially this needs to have a branch for even lower defect rates / larger codebases, where I’d use Idris.
Thanks for all the detail. That’s a lot of interesting stuff. You should make your own diagram! I’d like to see it.
I tried to cover cases like “for an interactive tool” under the CPU constraint, since that’s primarily what leads to the latency of Python, etc CLI tools starting up. My goal was to have a minimum number of decision point boxes.
I tried to cover cases like “for an interactive tool” under the CPU constraint, since that’s primarily what leads to the latency of Python, etc CLI tools starting up.
I find the difference is important enough to change the decision. Java is a fine choice for high throughput on a CPU-bound problem. But for an interactive tool (especially a CLI one) that needs to run quickly, Python may well be a better choice than Java.
I tried to cover cases like “for an interactive tool” under the CPU constraint
Yeah, there’s a big difference between “fast once the JIT is warmed up” and “fast to start”. For most languages that aren’t low-level they tend to be mutually exclusive.
Thinking about it my own diagram probably just has one box, Scala. Compare http://www.teamten.com/lawrence/writings/java-for-everything.html . I may still try to produce my own diagram but it would have to be some weird hybrid of trying to be objective while also pushing my own views.
Some very real decision points not covered:
These sorts of questions I think are more what I care about when choosing a language.
Like, the more I look at the flowchart, the more annoyed I get. Under embedded, you leave out assembly, Forth, and Ada–all of which have a strong case in certain types of highly-constrained environments.
Under Web, the notion of “large codebase” (what does that even mean?) seems to be used to justify static vs. dynamic typing, which is such a silly detail when you can make a better choice like “ES5, ES6, Elm, or ClojureScript”. Single-page app vs. widget is also a good question.
Integrating with the JVM can be done, incidentally, with native extensions and ~madness~cleverness, so while you are “stuck” with Java it may just be a wrapper.
I think there are some very strong questions, like mobile platform or hard real-time, that lead to one or (maybe) two options. But after that it’s mostly a non-technical decision based on what’s already in use to be integrated with and what the team knows or can cheaply learn.
Well, really, right now I’m more interested in learning Haskell, so the tails wags the dog. The programming language is Haskell and my decision tree is about which project to work on. Right now that’s practice problems and personal projects, but it’d be the day job in a heartbeat if there were opportunity.
I think you’re saying that at every “leaf” node in here, there’s a subjective choice that’s up to the preference of the team and their comfort? Right?
I agree with that in general. I think you could replace Python with Ruby and that’s fine. You could probably replace Java with another JVM language and have the same diagram. You could even choose Rust instead of C++ in all cases.
What I’m trying to pinpoint are the critical decision points that lead you down the tree one way or another, the things that aren’t subjective measures. Am I missing hard constraints? I’d especially like to figure out how a hard constraint can lead you to functional programming.
I’m saying that when I’m making decisions for clients/my employer I see a few forced decisions (iOS App? you’re using Swift), and I think your enumeration of hard constrains is solid. For the vast majority of line-of-business apps, the ALGOL derivatives (Python, Ruby, Java, C#, etc. etc.) are alike enough I’d decide on non-technical factors.
And in all seriousness, I’m interested enough in Haskell that I’m picking projects and jobs based on how well-suited they are to Haskell, not the other way around.
Someone has to be that girl, I guess:
Swift does not have garbage collection.
It has automatic reference counting, which has different tradeoffs.
Yes, I’ve occasionally encountered great resistance to this distinction. Thank you for saving me from saying it this time. :)
Yes you’re right. My point is that it’s got overhead + convenience, as opposed to bare-metal like C++.
The generic term you’re looking for is “automatic memory management”. :)
I’d have loved to see Clojure, F#, Haskell, Common Lisp, Scala, and OCaml. My company recently decided to start a new project, and after weighing the options you have listed, our top list was first Haskell followed by Clojure and Scala. We felt that choosing Java or C# when these faster to write and maintain languages exist with a wealth of high quality libraries and tooling would have been irresponsible.
That being said, my office is not afraid of training new hires, we’re just afraid of having to write 4-5x more code for features. We also would be astonished to grow much past our current size. We work in a complex domain, maintaining 800k+ LOC per person. We also have 1/3 already very comfortable with Clojure and F#, and one Haskell library contributor. Our hardest part wasn’t ruling out Ruby or Java, it was picking between Clojure, Core.Typed, Scala, OCaml, and Haskell.
An office that politically needs to be able to hire lots of developers (with any mix overseas) absolutely needs languages that “everyone knows”. Building critical infrastructure in Haskell is a choice to empower a few extremely talented developers, not onboard a lot of commodity offshore workers.
I’d have liked to see something to that political thread in your chart as well. If the company needs to be able to hire 50-100 developers at will, pretty much the only choice is Java (maybe Ruby). Many companies believe they have that need. If the goal is to have any offshore teams, again Java is pretty much the only choice. I’ve previously been there at a big company, and we were very much lambasted for even having some C#. It was clear offshore companies expect Java, and our higher ups were unpleasantly surprised when they found out it wasn’t all Java.
-- [Care about program correctness?] --+-- Yes --> (Clojure, F#, Haskell, Common
| Lisp, Scala, OCaml chart)
+--- No --> (Chart in post)
I can see what Haskell, OCaml and F# can bring in terms of improving program correctness (compared to the languages suggested in the chart), but I don’t think Clojure and Common Lisp have an upside on this topic.
A culture of immutability like Clojure’s tends to lead to a lower defect rate than the languages in the chart, though not as low as Haskell et al.
That’s quite a claim in my opinion. For example, one could argue that the “simple” static type checking provided by Java or Go catches more defects than the immutability promoted by Clojure.
culture of immutability like Clojure’s
Doesn’t help that much
– Used Clojure (and Datomic) in prod, wrote Clojure libraries
Types are more important.
Common Lisp does have algebraic data types, pattern matching, and type-checking, but you have to choose those features with asdf: https://chriskohlhepp.wordpress.com/metacircular-adventures-in-functional-abstraction-challenging-clojure-in-common-lisp/
Interesting. Is it commonly used?
Outside of that blog post, I have no idea.
That’s an invalid decision point isn’t it? The majority of all software doesn’t use that list and seems to work.
The majority of all software doesn’t use that list and seems to work.
Hehe. No. The majority of all software crashes at random every so often (along with more serious problems from time to time), and users get used to it.
It’s not quite as binary as craigstuntz makes it though - I’d probably phrase it as something like “do you need more than 3 9s of reliability?” (Though I do wonder how productive someone who was willing to write very fast and loose code in Haskell or similar would be - I don’t think I’ve ever seen anyone really try, but they might be even faster than Ruby et al).
The Linux kernel is in C and easily beats 3 nines. There are many other examples. I like that you’re suggesting a concrete decision point like that, though. That’s what I’m looking for.
The Linux kernel is in C and easily beats 3 nines.
With a big-company on-call rotation maybe. When I’ve run linux machines at home or in a small business I definitely didn’t get 3 nines out of them even just looking at kernel issues (that is to say, I had at least 1 day of downtime per 3 years that was directly attributable to a bug in Linux).
In any case my claim would not be that it’s impossible to get higher than 3 nines in C, only that it’s more expensive than doing so in the languages that craigstuntz lists (just as I don’t imagine that you intended to claim that e.g. it’s impossible to eliminate GC pauses in Java or run Python on iOS, only that doing so is more expensive than the languages you’ve nominated). If the level of reliability you get with naïvely written Python is high enough for your requirements then Python may well be the best choice. But if the level of reliability you need is at least the level that you get “for free” in naïvely written OCaml (a level that requires test coverage, static analysis, Valgrind or the like to achieve in the languages on your chant), then OCaml is probably a better choice.
If I check how often the Linux kernel fails to reinitialize a connected display, mount an SD card, powers down connected USB devices, I am with lmm that it works but not really reliably.
A lot of that depends on what you mean by ‘work’. If you mean ‘really work exactly correctly’ then I would put the upper bound on non-trivial programs written in C as zero 9s - but then you really need your program to be formalised in something like Coq or Agda to get to this kind of level, so I’m okay with people glossing over this. If you mean ‘work correctly’, then I would put the upper bound on non-trivial programs written in C as less than one 9 - Linux for example has had vulnerabilities present for more than one out of every ten units of time. But perhaps you don’t need your code to actually work, you just need it to kind of, sort of work - then you can probably get up to two 9s with C. I wouldn’t count on it though. For example, I have lost more than one day out of every 100 to bugs in the linux kernel over the time period I have been seriously using it, and the linux kernel is abnormally good as far as C programs go. Or perhaps you are simply only going to count complete outages, in which case sure, it doesn’t matter what you program in, the hardware is the limiting factor.
I couldn’t actually decide whether I was joking or not, but I think my point might not have been “you should use Clojure/…” but rather “I’m struck by the absence of suitability for any domain other than performance or runtime constraints from your chart.”
Assume that I’m ignorant. How do you know your problem lies in those other domains? What question can I ask myself to decide?
I like to ask: “What kind of bugs do I want to have in the production system?” (People might glibly answer “None,” but it’s setting a high bar.)
Set quality constraints, and choose a system of tools (languages, formal methods, static analysis, fuzzing, testing, etc.) to match.
“Seems to work” is a far cry different from “provably correct” (which is what programs with strong static analysis strive for).
So you’re saying all of these languages are as provable as a tool like Coq?
In no way did I say that. The point was many popular languages (some of which were included in the chart) are designed in a way that limits the effectiveness of external static analysis tools, and provide little static analysis themselves. There are other languages (like OCaml, Haskell, Rust, etc.) that provide stronger static analysis guarantees (each language in its own way, eliminating its own potential source of incorrectness). Strength of static analysis is a continuum, and my point was that many of the languages in the linked-to chart are on the lower end of the spectrum. If we have the opportunity to use static analysis to provably reduce uncertainty about correctness, isn’t it a good idea to so (under the admittedly potentially dubious assumption that all other things are equal)?
Okay that’s helpful. So “ease of static analysis” is what you want, right? When is that most appropriate? How do you choose? Is there a quesrion about the problem domain that leads to the need for that? Maybe “algorithmic complexity”?
I would answer the following questions like so:
Static analysis is always useful. The question is what sort of guarantees you would like the language to provide. If your concern is guaranteed memory safety or freedom from data races, then Rust would be a good fit. If your concern is guaranteed data validation via strong typing guarantees, then maybe Haskell is the right choice. Each language provides its own guarantees and associated trade-offs.
LiquidHaskell lets you prove certain properties in Haskell code.
[Comment removed by author]
Sorry, I mean that it’s not a concrete choice. People who use other languages create correct programs. People who use functional languages can also create incorrect programs. Everyone is trying to do it right and be productive.
Obviously the biases of my own experience apply, but Scala or F# feel a lot more enterprise-approved than Go or Typescript.
I agree that Scala can sound more enterprise-approved than Go or TypeScript, but I disagree about F# which is not “mainstream” enough to fit this category.
I think that’s an uncharitable interpretation, as the author asks for suggestions and modifications to the tree. It’s more likely that the languages included here were chosen simply because they’re the ones with which the author is more familiar.
Our hardest part wasn’t ruling out Ruby or Java, it was picking between Clojure, Core.Typed, Scala, OCaml, and Haskell.
How did you get to that point? My goal is not to make a political statement. I’m mapping the decision points for me. I can’t come up with a decision point that’s concrete that leads to that list of languages. The only questions that do are “existing familiarity”, which is subjective.
If you know the right question for that I would love to hear it.
our top list was first Haskell followed by Clojure and Scala
That’s kinda surprising, because every way I twist it, I can’t see them in a similar class:
What was your niche that put them in the same class?
Also, to fight back a bit with the Clojure mushy data structures we plan to use Prismatic Schema, which provides a “test-time” run time shape checking and value type assertions. While not a solution we love, we feel like picking a “best in class” tool, and to us, Scala very much does not feel like a choice we are comfortable with. We actually had Frege, the JVM Haskell before Scala. Unfortunately, that seems a little new and uncommon for us.
I’m not quite sure I follow your meaning of “best in class”, but if you’re looking for the features of Scala in an elegant, coherent design, I think that language is Ceylon. (Personally I’ll put up with the warts of Scala for the sake of the library and tool support, but you may take the other view).
Thanks for the info! Our desired features of Scala are only those that are also found in Haskell. I’ve not looked into Ceylon, but I suspect if we wanted to use something that far “off the beaten path”, we’d just go with Frege. We felt like, for the JVM, neither Frege nor Scala presented as compelling of a tool as Clojure, which is an excellent “JS-like” for those that love the REPL, macros, fantastic json serving, and “mushy” data structures. While totally different from Haskell, it is incredibly powerful in its own right, and offers a lot of developer productivity.
If you need to integrate with the JVM, Scala and Clojure are strong options. The only time Java is the right choice is when you’re using the JVM but have extremely low latency demands, and low-latency Java doesn’t look anything like regular Java. You’re essentially using Java as a C at that point, because you want GCs to happen very rarely to never.
I also don’t know why you’d prefer C++ over C. C++ is a bloated mess of a language. I might be tempted to say that it’s better to work in C. If you need your own safe string type and would normally go to C++ for that, try writing your own. It’s not that hard.
The better statically typed languages (e.g. Haskell and Elm) are conspicuously missing, but that’s already been covered here. Erlang is missing; I’m not an expert on it but I understand that it has a strong use case.
I’m also not entirely convinced that if (presumably external) IO is your bottleneck that Python is the most desirable language. (And why is Go on the list? Go was designed to be a large corporate language. For Google, the design choices make sense. Under 500kLoC, I’m not convinced that it offers much other than go fmt and the decision (which I strongly agree with) to make spurious imports an error. Otherwise, I’d replace Go with Haskell. If you can afford a GC, you probably want Haskell.
I’d expand the “Python” node which seems to correspond with “no major constraints and you can use the language you like best” (which is a very common use case, because 50% of what we do isn’t embedded and neither is 50% of the rest “real time”) as follows:
I really like your “short” -> “medium” -> “long” term progression. It matches pretty much exactly my current feelings, good call.
main choices (most of my hobby projects are small cli or desktop apps)
I haven’t done much web programming, but I’ve been following elm’s development with great interest; some combination of elm and elixir would probably get my vote for a large project, and perhaps ur/web for a smaller one.
languages i don’t use for relatively minor reasons:
languages i don’t use but would like to at some point:
The biggest problem I see with this chart (and the ones like it) is that it makes it seem as if there’s no overlap between those languages' usecases.
Perhaps multiple charts are needed, one for each of:
Admittedly I don’t know a lot about gaming development (even less for consoles), but I don’t think everybody uses C#.
Agreed. I’m pretty sure games for any of the Playstations nor any of the Nintendo systems are most certainly not written in C#.
You might be surprised, because Unity does exactly that in the case of the Playstation.
If it’s parsing or language implementation I reach for Haskell, because I haven’t been able to fond anything better than parsec* for writing parsers, and recursive algebraic data types lend themselves really well to ASTs.
* actually megaparsec, a maintained fork of parsec that fixes several issues and annoyances in the original
The economics of software, and I think this diagram belongs there, is a really interesting field. Other things to consider:
Depending on circumstances, some of these issues can also become important.
Another consideration in this vein are the skills of people working on the project. If everyone has a common language with a clear path to a working solution then it’s hard to justify switching languages in certain situations. “Who” often decides how.
Good point! I assumed that the project is started from scratch but if a company already has engineers on board then it’s much cheaper to put them to work.
no mention of Scala or Haskell… or any functional language…
You are trying to take a complicated problem too much. Now, I do normally advocate the “simplicate and add lightness” approach, but I don’t think you can do it with a decision tree like this.