1. 17

Would be interesting to get Lobsters' take on this. Thanks!


  2. 18

    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?”[1] 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.

    [1] 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.

    1. 1

      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.

      1. 5

        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.

        1. 1

          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.

          1. 1

            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.

        2. 17

          Some very real decision points not covered:

          • Is the program run online or batch–does it matter if it takes a while to spin up?
          • Is the program being written by multiple people?
          • Is the program a library or an application…e.g., does it need to be able to link with other programs?
          • Is the program going to be maintained or deployed once and forgotten–do we have to do our best work first?
          • Is the program going to face rapidly-changing requirements–are we going to be fighting our own codebase frequently?
          • How long is this program going to be around for?
          • Is the program going to have to deal with arbitrary input from the real (and hostile!) world?

          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.

          1. 11

            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.

            1. 3

              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.

              1. 5

                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.

            2. 7

              Someone has to be that girl, I guess:

              Swift does not have garbage collection.

              It has automatic reference counting, which has different tradeoffs.

              1. 2

                Yes, I’ve occasionally encountered great resistance to this distinction. Thank you for saving me from saying it this time. :)

                1. 1

                  Yes you’re right. My point is that it’s got overhead + convenience, as opposed to bare-metal like C++.

                  1. 3

                    The generic term you’re looking for is “automatic memory management”. :)

                2. 11

                  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.

                  1. 16
                    -- [Care about program correctness?] --+-- Yes --> (Clojure, F#, Haskell, Common 
                                                           |            Lisp, Scala, OCaml chart)
                                                           +--- No --> (Chart in post)
                    1. 7

                      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.

                      1. 3

                        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.

                        1. 12

                          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.

                          1. 4

                            culture of immutability like Clojure’s

                            Doesn’t help that much

                            – Used Clojure (and Datomic) in prod, wrote Clojure libraries

                            Types are more important.

                          2. 2

                            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/

                            1. 1

                              Interesting. Is it commonly used?

                              1. 1

                                Outside of that blog post, I have no idea.

                          3. 2

                            That’s an invalid decision point isn’t it? The majority of all software doesn’t use that list and seems to work.

                            1. 10

                              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).

                              1. 3

                                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.

                                1. 7

                                  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.

                                  1. 3

                                    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.

                                    1. 1

                                      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.

                                  2. 3

                                    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.”

                                    1. 4

                                      Assume that I’m ignorant. How do you know your problem lies in those other domains? What question can I ask myself to decide?

                                      1. 14

                                        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.)

                                        • Are lives or personal safety on the line?
                                        • Is the user’s PII on the line?
                                        • How much money can your employer afford to lose if it goes wrong? (Will be different for, say, trading software vs. internal org-chart printer.)

                                        Set quality constraints, and choose a system of tools (languages, formal methods, static analysis, fuzzing, testing, etc.) to match.

                                    2. 3

                                      “Seems to work” is a far cry different from “provably correct” (which is what programs with strong static analysis strive for).

                                      1. 3

                                        So you’re saying all of these languages are as provable as a tool like Coq?

                                        1. 4

                                          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)?

                                          1. 3

                                            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”?

                                            1. 3

                                              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.

                                          2. 2

                                            LiquidHaskell lets you prove certain properties in Haskell code.

                                        2. [Comment removed by author]

                                          1. 2

                                            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.

                                      2. [Comment removed by author]

                                        1. 6

                                          Obviously the biases of my own experience apply, but Scala or F# feel a lot more enterprise-approved than Go or Typescript.

                                          1. 2

                                            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.

                                          2. 2

                                            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.

                                          3. 2

                                            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.

                                            1. 2

                                              We have a team that between the 9 of us have worked professionally in Java, C#, F#, Ruby, Python, Perl, PHP, ColdFusion, Actionscript, Javascript, Clojure, Scala, Go, VB6, and VB.NET. Many have worked in several of these. When it came time to build something large and new, we considered our options. Most of us feel at this point that building something in a stack that optimizes for onboarding speed (Java, Go, JS, Python, Ruby) does not make sense for our small, slowly changing team of experts. We also feel that our list of languages are those that provide the longest lever for the skilled user to most effectively meet the business needs quickly with the greatest maintainability.

                                              We ruled out OCaml, which while very similar to our well known F#, is a new runtime and isn’t as compelling to us as Haskell. We’ve been writing our F# more like Haskell anyway, so that wasn’t a big leap. Clojure is a “best in class” tool that provides high power for those of the crew that like the feel of a better Javascript. “Clojure is just ES10 with s-expressions” is the comment. We also ruled out Scala, as several of us feel it is the unfortunate intersection of the most complex parts of both Haskell and Java. That left us with Clojure or Haskell. Both we feel are best in class for their “category”: Clojure is so fast to write and build due to the fantastic REPL and macros, and Haskell is the best to maintain long term due to purity and powerful abstractions. We are still in the process of testing the two, but we think we will choose Clojure due to the fact that we know it better, and we are politically pressured to provide a JVM solution.

                                            2. 1

                                              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:

                                              • JVM integration: Clojure, Scala but not Haskell
                                              • Typed functional programming: Haskell, Scala but not Clojure

                                              What was your niche that put them in the same class?

                                              1. 1

                                                See: https://lobste.rs/s/umexqb/how_do_you_choose_the_programming_language_for_a_project/comments/yx79yu#c_yx79yu

                                                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.

                                                1. 1

                                                  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).

                                                  1. 1

                                                    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.

                                            3. 11

                                              Disagree strongly.

                                              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:

                                              • short projects: Python almost always wins. Why? Libraries. There’s a reason why it’s the language of choice for exploratory data science. Also, while I like static typing for production systems that will live a long time, dynamic has a speed advantage when you’re learning something brand new (but static typing helps you get your knowledge down pat).
                                              • medium-term projects: probably Clojure. It’s dynamically-typed but it has a conservative culture. Also, its community is fucking awesome. Since it lives on the JVM and has had a lot of person-hours put into making it possible to write fast code, you can probably get to 1.3x Java performance which is usually good enough. The fact that Clojure developers are better than all but a few percent of Java programmers (the low latency crowd) also helps.
                                              • long-term projects: Haskell. If it’s going to go multi-developer, you probably want compile-time typing. If it’s going to live a long time, performance will matter at some point. You may even want access to people like me who are so anal about correctness that we go and fucking learn stuff like category theory in our 30s. Haskell’s strong/static typing also forces you to think about APIs and makes you a better designer. The lack of a sizeable community (as there is for Clojure or Python) isn’t as big of a deal on the long projects, since rolling your own stuff tends to be a fixed cost (see: Jane Street’s success with OCaml).
                                              1. 3

                                                I really like your “short” -> “medium” -> “long” term progression. It matches pretty much exactly my current feelings, good call.

                                              2. 6

                                                main choices (most of my hobby projects are small cli or desktop apps)

                                                • quick, exploratory programming: ruby
                                                • understand the problem, want the comfort of static typing: ocaml
                                                • need to work with the jvm: clojure

                                                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:

                                                • i like the idea of rust, but for what i do, ocaml inevitably ends up being more pleasant to work with (rust code looks a lot more verbose, particularly having to use unwrap() all over the place)
                                                • i love the experience of writing in racket, but the gui framework takes up well over 100M of RAM to run even small applications

                                                languages i don’t use but would like to at some point:

                                                • spent several days trying to get the latest f# working on linux, finally gave up
                                                • tried using d a few times, the tooling was a mess. gave up.
                                                1. 4

                                                  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.

                                                  • According to this chart the client to my Dropbox-clone may be written in Python, but I still might choose some compiled language due to deployment constraints (want self-cointained binaries with reasonable size, and without a lot of effort).
                                                  • Calculations for some research I might be doing might be CPU-bound, but I still might choose Python because numpy and pypy make my program reasonably fast.
                                                  • Admittedly I don’t know a lot about gaming development (even less for consoles), but I don’t think everybody uses C#.

                                                  Perhaps multiple charts are needed, one for each of:

                                                  • “Which language has the best deployment strategy (for my purposes)?”
                                                  • “Which language has the best runtime properties (for my purposes)?”
                                                  • “Which language hast the best tooling (for my purposes)?”
                                                  • …?
                                                  1. 2

                                                    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#.

                                                    1. 1

                                                      You might be surprised, because Unity does exactly that in the case of the Playstation.

                                                  2. 3

                                                    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

                                                    1. 3

                                                      The economics of software, and I think this diagram belongs there, is a really interesting field. Other things to consider:

                                                      • the availability of talent
                                                      • the community
                                                      • the availability of libraries (For example, there’s no SymPy for Ruby)
                                                      • the longevity and maturity of the platform

                                                      Depending on circumstances, some of these issues can also become important.

                                                      1. 1

                                                        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.

                                                        1. 1

                                                          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.

                                                      2. 3

                                                        no mention of Scala or Haskell… or any functional language…

                                                        1. 2

                                                          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.