1. 62

A killer app is software that’s so good it justifies using specific hardware or operating system. By analogy, a killer library is a library so good it justifies using a specific programming language. Two examples:

  • Ruby on Rails was so good for webdev at the time it justified using Ruby.
  • Pandas is so good for datascience it justifies using Python.

Usually there are alternatives to the killer library in other languages, but they don’t catch on for one reason or another— usability, lack of critical features, etc. This can change over time. Arguably quickcheck could have been a killer library for Haskell, but now most languages have a decent PBT framework.

What are some killer libraries for your language of choice?

    1. 55

      The standard library in Go.

      1. 30

        I have done lots and lots of Java and Python in my career before I used go. I honestly find the go stdlib just okay. There is usually something that does the trick, but I am not a super fan. I am also not buying this consistency thing. I deal a lot with strings unfortunately and this mix of fmt, strings, strconv,bytes to do anything with strings is not intuitive. I understand where go is coming from historically and from a design philosophy yet I don’t find it that superior.

        (personally I would love to see a language that is a bit more high level/expressive like python, but with the go runtime/deployment model)

      2. 7

        I started a security project specifically because of high quality cryptography code in the standard library like no other language.

      3. 13

        You mean the one where you can’t multiply a duration by a number, but you can multiply a duration by a duration?

        1. 3

          Maybe I’m missing something, but dur := time.Hour * 2, as well as dur := 2 * time.Hour compile just fine.

          1. 6

            The literal is being implicitly converted to a duration. Try it with a variable instead of 2.

            1. 5

              Got it. However, that’s not really a limitation of the standard library, but rather a limitation of the language that prevents implicit type casting.

              1. 7

                The point is that mathematically, multiplying a number with a duration should work, whereas multiplying a duration with a duration should not.

                1. 2

                  It never occurred to me that people would expect to be able to multiply an int by a duration and not multiply two durations together. Personally I’m grateful that Go doesn’t implicitly convert ints to durations or vice versa–I suspect this has prevented quite a few bugs.

                  1. -4

                    Have you ever had physics in school? You might want to repeat it.

                    I’m not talking about implicit conversions.

                    1. 3

                      I think the physics repeat remark might be a little heated for this context: we can all take a breath here and try to understand each other.

                      I’m personally of the opinion that multiplying an int by a duration implicitly is a bit of an anti-feature: I expect it to work in loosey-goosey languages like Python or Ruby, I even expect it to work in languages like Rust where the Into trait lets someone, somewhere, define explicitly how the conversion should occur (this starts getting into the realm of the newtype pattern from eg. Haskell), but I don’t expect two disparate types to multiply or add together, no, regardless of what those are.

                      To be extra clear: I think Into is the correct way to solve for the expected ergonomics here, and wish more languages had this type of explicit control.

                      1. 12

                        Well, thing is:

                        • Adding two durations is obviously okay.
                        • So is subtracting two durations.
                        • Negative durations are okay too.
                        • Adding a duration to itself n times is okay.
                        • We just defined multiplication of durations by natural numbers. Therefore it is okay.
                        • Since negative durations are a thing, we can extend this to relative numbers too.
                        • Actually, multiplication can be extended to real numbers as well.
                        • All real numbers except zero have an inverse, so it’s okay to divide durations by any non-zero number.

                        On the other hand:

                        • It is not okay to add (or subtract) a duration and a number together.
                        • It is not okay to multiply (or divide) a duration by another duration.

                        So if I want to be super-strict with my operations and allow zero implicit conversions, I would have the following functions:

                        seconds s_add(seconds, seconds);
                        seconds s_sub(seconds, seconds);
                        seconds s_mul(seconds, double);
                        seconds s_div(seconds, double);
                        

                        Or if we’re in something like ML or Haskell:

                        s_add : seconds -> seconds -> seconds
                        s_sub : seconds -> seconds -> seconds
                        s_mul : seconds -> real -> seconds
                        s_div : seconds -> real -> seconds
                        

                        Now the binary operators +, -, *, and / are functions just like any other. We can just overload them so they accept the right operands. We have such an overloading even in C: adding two floats together is not the same as adding two integer together at all, but the compiler knows which one you want by looking at the type of the operands. (It also has the evil implicit conversions, but that’s different.)

                        So while a language that allows multiplying a duration by a number looks like it is implicitly converting the number to a duration before performing the multiplication, it absolutely does not. That’s just operator overloading: because you really want to multiply durations by ordinary numbers. And since multiplying two durations together makes no sense, you should get an error from your compiler if you try it.

                      2. 7

                        Again, multiplying a duration by a number is not “loosey-gooey”. Multiplying a duration by a duration is “loosey-gooey”, unless the result is a duration squared, which it isn’t.

                        1. 1

                          I think it depends on what you believe types are for—are they exactly units, or are they constraints (or both)?

                          1. 3

                            No matter if you treat types as units or constraints, you want to have operations that make sense. Multiplying 3 seconds by 5 hours doesn’t mean anything (except in the context of physics, where it can be an intermediate value).

                            1. 1

                              Agreed that you want operations that make sense, but if you think of types as units, then you probably want to be able to multiply ints and other types. If you think of them as constraints (especially for avoiding bugs) you probably don’t want to be able to multiply ints and arbitrary types. Personally, I’m more concerned with avoiding bugs rather than a strict adherence to mathematicians’ semantic preferences. There’s nothing fundamentally wrong with the latter, but it seems likely to produce more bugs.

                              1. 3

                                How exactly does allowing durations to be multiplied with each other, while not allowing them to be multiplied by integers, allow you to prevent bugs? If anything, I’d say it can introduce bugs.

                                you probably don’t want to be able to multiply ints and arbitrary types.

                                Where did I say anything about multiplying integers with arbitrary types?

                                1. 1

                                  How exactly does allowing durations to be multiplied with each other, while not allowing them to be multiplied by integers, allow you to prevent bugs?

                                  It means we can’t accidentally multiply a duration field by some integer ID field (or a field of some other integer type) by accident. In general it stands to reason that the more precise you are about your types, the less likely you are to have bugs in which you mixed two things that ought not have been mixed, and Duration is a more precise type than int. I’m not familiar with any bugs arising from being too precise with types, and even if they exist I suspect they are rarer than the inverse.

                                  Where did I say anything about multiplying integers with arbitrary types?

                                  Presumably you aren’t advocating a type system that makes a special exception for durations and ints, right? Feel free to elaborate about what exactly you’re advocating rather than making us guess. :)

                                  1. 3

                                    It means we can’t accidentally multiply a duration field by some integer ID field

                                    That’s why you use a different type for the ID.

                                    In general it stands to reason that the more precise you are about your types, the less likely you are to have bugs in which you mixed two things that ought not have been mixed, and Duration is a more precise type than int.

                                    Preciseness is only good when the typing is correct.

                                    Presumably you aren’t advocating a type system that makes a special exception for durations and ints, right?

                                    No, I’m advocation for a system that allows you to define multiplication however it makes sense. Like in Python. Or Nim. Or even C++, though C++ is partially weakly typed because of the C heritage.

                                    1. 1

                                      That’s why you use a different type for the ID.

                                      I agree, I’m advocating for precise types. But in any case you seem to be okay with “untyped” ints for quantities/coefficients so we can use the example of mixing up coefficients of durations with coefficients of some other concept.

                                      Preciseness is only good when the typing is correct.

                                      Agreed, and Go gets the typing correct, because types aren’t units. 👍

                                      No, I’m advocation for a system that allows you to define multiplication however it makes sense. Like in Python. Or Nim. Or even C++, though C++ is partially weakly typed because of the C heritage.

                                      My background is in C++ and Python. Very little good comes out of operator overloading but it opens the door for all kinds of clever stuff. For example, Sqlalchemy overloads operators (such as ==) to allow for a cutesy DSL, but a bug was introduced when someone tried to use a variable of that type in a conditional. I’ve never heard of bugs resulting from a lack of overloading, and it’s easy to workaround by defining a Multiply() function that takes your preferred type. No surprises, precise, and correct. 💯

                                      Moreover, the canonical datetime libraries for C++ and Python don’t give you back “DurationSquared” when you multiply two durations, nor do they allow you to divide a distance by a duration to get a Speed because types aren’t units–you could overload the multiplication operator to support duration * duration or overload the division operator to support distance / miles, but you have to model that for every combination of types (at least in mainstream languages like C++, Python, Go, etc) and for no benefit that I’m able to discern (apart from “to allow certain types to behave sort of like units”, which doesn’t seem like a meaningful goal unto itself).

                      3. 3

                        In rust

                        • The Into trait does not do automatic coercion. The Deref trait does under the right circumstances, but you shouldn’t use it to do so here (it’s really just meant for “smart pointers”, though there is no good definition of what a smart pointer is other than “something that implements Deref”).

                        • Traits like Add and Mul take both a lhs and a rhs type. For Add those would both be Duration. For Mul I would strongly expect it to take a Duration on one side and an int/float on the other.

                        Multiplying two duration’s together makes little sense. What is “2 seconds * 10 seconds”? Units wise I get “20 seconds^2” (which unfortunately most type systems don’t represent well). Physical interpretation wise time^2 is mostly just an intermediate value, but you could visualize time as distances (e.g. with light seconds), in which case it would be an area. Or alternatively you might notice that you divide distance by it you get an acceleration (m/s^2). What it definitely isn’t is a duration.

                        Multiplying a duration by a unit-less quantity (like an integer) on the other hand makes perfect sense “2 seconds * 10” is an amount of time 10 times as long. Hence why I would Duration to implement Mul with the lhs/rhs as ints.

                    2. 1

                      Sorry, my remark wasn’t meant to be provocative. I’ve just spent so much more time in the programming world than the math or physics worlds, hence “it never occurred to me”.

        2. 2

          You can find rough edges in every language and every (standard) library. This is unfortunately a fact of developer life.

          1. 2

            I wouldn’t call this a rough edge, but a fundamental flaw of the type system.

        3. 2

          In retrospect, time.Duration should have been an opaque type and not a named version of int64 (as the person who added Duration.Abs(), I’m well aware of the pitfalls), but there are no mainstream languages with the ability to multiply variables of type feet by feet and get a type square feet result, so I wouldn’t blame Go for that particularly.

          1. 2

            there are no mainstream languages with the ability to multiply variables of type feet by feet and get a type square feet result, so I wouldn’t blame Go for that particularly.

            Well yes, but it should be an error.

            1. 1

              There are also no mainstream languages where it’s an error. I agree though that it should have been type Duration struct { int64 } which would have prevented all arithmetic on durations.

              1. 2

                It’s an error in rust

                fn main() {
                    let dt = std::time::Duration::from_secs(1);
                    dt * 2; // ok ("warning: unused arithmetic operation" technically)
                    dt * dt; // error: mismatched types. Expected `u32` found struct `Duration` (pointing at second operand)
                }
                

                https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bfd82c951af32f237ff3fcd568be5f75

                1. 1

                  In Rust, Duration is a struct, not a bare int. The multiplication works through operator overloading, which allows u32 but not another Duration. I take the point that this is better than Go.

                  As I said above, it would be better if in Go Duration were type Duration struct { int64 }. Go doesn’t have operator overloading, so you wouldn’t be able to multiply at all, but you’d have to use methods, like d.Mul(3) etc. It would be worth it though because then those could saturate when they overflow instead of wrapping around. It’s a minor language wart.

              2. 2

                Python does it correctly.

                from datetime import timedelta
                hour = timedelta(hours=1)
                print(hour)
                print(3*hour)
                print(hour*hour)
                

                Output:

                1:00:00
                3:00:00
                TypeError: unsupported operand type(s) for *: 'datetime.timedelta' and 'datetime.timedelta'
                

                Attempt This Online!

                1. 2

                  Yes, but Python itself does not use timedelta, which sucks.

                  >>> time.sleep(datetime.timedelta(0,0,1))
                  TypeError: 'datetime.timedelta' object cannot be interpreted as an integer
                  
      4. 1

        I come from python world but would love to hear more about what’s so special about Go standard library

        1. 4

          I would say the quality is more consistent across modules than Python’s, which feels like it evolved more organically. There’s also some foundational concepts like a stream of bytes that’s simpler and more composable than file-like objects in Python

          1. 2

            Python is 3 times the age of Go. 20 yrs from now I hope we say the Go stdlib is as consistent as it is now. I also hope to be retired by then and hope terminated before then.

            1. 3

              I remember when people were saying this about Go 5 and 10 years ago. It’s been almost 15 years and Go has done very well at consistency in its stdlib and elsewhere. When python was nearly 15 it’s standard library was already a mess—Go had the benefit of hindsight, specifically with respect to how “kitchen sink” languages like C++ turned out.

              1. 1

                Some of the worst things in Go’s standard library are old RPC formats and container types, but there’s not too much of it.

        2. 2

          It’s better organized and more cohesive. For example, what’s the difference between import os and import sys? I really couldn’t tell you. You just have to memorize it.

          The json module has load and loads, but I’m not aware of any other packages that follow that convention for file-like vs string input. Anyway, why not just have one function take both and branch on the type? Go is more consistent about using their file-like types (io.Reader/Writer) everywhere.

          Time in Python is 💩. No one ever wants a time without a time zone! Go libraries all take a time.Duration instead of this one taking seconds and that one taking milliseconds. Python has a timedelta type but no one uses it.

          The various urllibs are all strictly worse than their Go equivalents.

          Python does have better itertools and collections though. Now that Go has generics, hopefully, those will get ported over too.

          1. 2

            Honestly I hate the whole datetime.datetime.now() thing. I also don’t love that Python is so inconsistent with its casing conventions (e.g., testing uses assertEqual, datetime uses timedelta, and most other things use UpperCamelCase for types and snake_case for functions and methods). Also I’ve done a lot of subprocessing in Python and I still have to consult the docs every single time for subprocess.run() and friends–the arguments are just dizzying. Also, despite being a very dynamic language, Python doesn’t have anything as convenient as Go’s json.Marshal()–you have to write to_json() methods on every class that return a JSON-like dict structure (and to be quite clear, I have grievances with Go’s JSON package). Similarly, Python’s standard HTTP libraries are more tedious than those in Go–the canonical advice is just to import requests or similar, but this is a pain for simple scripts (e.g., I now have a build step for my AWS Lambda function which pretty much erases the benefit of using Python over Go for Lambda in the first place). These are just a few examples of issues with the Python stdlib off the top of my head, but there are lots more :)

    2. 43

      Rust’s serde is definitely a go-to for me.

      Part of why writing simple network services in Rust is so nice is that Serde + any of the high-level web server crates make it the work of literally minutes to define a simple data structure and accept + emit it in a safe, validated way. I can more or less ignore the wire format until I start hitting really edgy edge cases.

      1. 3

        But the same could be said of haskell’s aeson library (surely the main inspiration for serde), so it must be something else that is the “killer” argument to choose rust instead.

        1. 12

          aeson is only for json, serde is format agnostic

    3. 37

      Honestly one of the biggest reasons I gravitate towards Elixir is because of OTP itself.

      1. 4

        Also the Elixir standard library is exceptionally pleasent to work with. Modern and well thought out.

        1. 4

          Also the Elixir standard library is exceptionally pleasent to work with.

          Agreed, especially with protocols such as Inspect and Enumerable.

          Modern and well thought out.

          One of the things I appreciate most is what I perceive as a lack of surprises.

      2. 1

        But does that really work in this case? You can do web stuff with pure Erlang, it’s just kinda painful compared to Elixir and there are many workloads (without the web involved) where I don’t see a huge benefit. But agreed on a killer library for BEAM languages, close enough.

        1. 1

          But does that really work in this case?

          Yes, OTP really works in my cases.

          there are many workloads (without the web involved) where I don’t see a huge benefit.

          Most of the Elixir code I have written hasn’t involved the web (or has been only incidentally web-related) and I’ve still chosen Elixir because of OTP. For concurrency, reliability, etc. it doesn’t get much better for the problems I’ve tackled with it.

          But agreed on a killer library for BEAM languages, close enough.

          I don’t know enough about other BEAM languages like Gleam, LFE, or Clojurel and if/how they do or do not use OTP.

          1. 1

            My bad, I meant “does framing OTP as a killer library in this case work because it’s not Elixir-only” ;) But you cleared it up.

    4. 23
      • Qt for C++
      • Swing/JavaFX for Java
      • numpy for Python
      • Eigen for C++
      • Unity for C#
      • Flutter for Dart
      • stdlib for Go
      • LaTeX for TeX
      • (vaguely gestures in BLAS direction) for Fortran
      • (vaguely gestures) for R
      1. 13

        ggplot2 for R… (though plotnine for python is a pretty decent port)

        1. 6

          I’d say the whole tidyverse is the killer feature of R, and it has great docs too:

          https://www.rstudio.com/wp-content/uploads/2015/02/data-wrangling-cheatsheet.pdf

          The equivalent Python and Julia libraries, including Pandas, are largely copying what tidyverse does, often in a less elegant and composable way.

          The origin story of Pandas was basically that the creator couldn’t stand R the language, and wanted to do the same thing in Python. I agree R the language is hard to learn, but tidyverse (including ggplot2) makes it worth it.

          1. 1

            I tried to see if ChatGPT could explain R APIs, and at least for my examples it failed horribly.

            Whether that says something about the language model, or R’s documentation, is anybody’s guess.

            1. 5

              This “Tidy Data” paper by author Hadley Wickam is a great way to understand the tidyverse, which is much cleaner than R’s APIs:

              https://vita.had.co.nz/papers/tidy-data.pdf

              Also see references in my blog post: What Is a Data Frame? (In Python, R, and SQL)

              ChatGPT can spit back common knowledge, but it doesn’t really understand anything, so I wouldn’t expect it to do well on this task.

              It frequently makes up answers out of the blue, so if you don’t know something, you can’t rely on it. It’s only useful when you already know the answer but are perhaps having problems recalling it.

            2. 1

              If you’re interested: what did you ask it to explain, about which task / package / function signature?

              With that information, I can probably tell you whether the function has bad documentation (they definitely exist); if not, it’s a ChatGPT problem. Without that information, the answer is anybody’s guess :-)

              1. 1

                The one I recall: using devtools::install_github, how to prevent it from asking about updating other packages.

      2. 5

        I think the killer ones for Python are GPU-accelerated NumPy replacements, i.e. PyTorch and JAX. This is what really sets Python apart from other languages in terms of ML support.

        I disagree with @hwayne on this one, Pandas is IMHO clunky and slow compared to R data.table or DataFrames.jl. I never found anything special about it.

        I am surprised to see no mention of Simulink, which is a big competitive moat for MATLAB. I don’t think there’s anything else comparable in the domain of dynamic system simulation.

        Mathematica’s standard library is really broad and well documented. Probably one of the best pieces of proprietary source code out there, both in terms of functionality and quality.

        Others have already mentioned OTP, which would be another candidate.

      3. 5

        Nit: s/QT/Qt/

        QT is used for QuickTime.

      4. 3

        Interesting that you mention Eigen. I suppose I didn’t use it to its full potential, just for some vector math and it was… fine? but not outstanding or one of a kind to me.

      5. 1
        • for Swing/JavaFX. The interoperate well so you can use the best of both. Recently I updated a Swing app using JavaFX properties. Saved some ugly refresh code. + I love CSS support in JavaFX! I can stay focused on layout and code and then deal will the styles later.
      6. 1

        Seconding numpy (and also scipy!) for Python.

        Recently I’ve been writing Rust code that does lots of math in 3D space, and although the performance is excellent (the reason I chose Rust!), I do find some of the math repetitive: I find myself thinking “I shouldn’t have to write an iterator-pipeline/for-loop just to multiply and average these vectors.” In retrospect, maybe I should have used nalgebra, but having never used it before, I have no idea how it compares to numpy.

    5. 16

      I kept slashing my list until I was left with Qt. I know a lot of really good libraries for all languages but realistically there are probably good alternatives for all of them if you look outside their language’s ecosystem.

      Qt is hands down the one killer library I know. There’s no native application framework of comparable flexibility and breadth that’s so well documented, so easily obtainable, and cross-platform. Windows, macOS and Android all have better “local” frameworks but Qt is light years ahead of anything that runs on all of those, and pretty much the only thing I’d use for writing Linux/BSD software.

      What was also on my list but I don’t really think it qualifies:

      • C++: ZeroMQ (but it has bindings for, like, everything, so I dunno if it counts)
      • Python: SciPy (it’s amazing but honestly so is Matlab if you can afford it)
      • C#: Unity, for all its faults (but it’s in a weird category)
      • At one point Cocoa (and, to some degree, GNUStep) was worth learning Objective-C for. That’s probably still the case for Swift but I haven’t tried it
    6. 15
      • ggplot is so good for defining plots, it more than justifies using R. It is so freeing to be able to map data variables directly to plot aesthetics like x-position, y-position, stroke colour, fill colour, opacity, stroke width, shape, whisker length, ribbon width, etc. without needing to manually construct a vector of colour names or what have you.
      • tidyr and dplyr are so good for tidying and then wrangling data: either of these, too, justifies using R. Both have a deep understanding of their problem domain’s inputs, actions, and outputs; a small core of orthogonal verbs that ninety percent of the time suffices on its own; and a wide selection of variant verbs that come in useful the other ninety percent of the time.

      NB: Pandas is a killer library in the opposite sense: its is so painful for interactive data analysis that it justifies avoiding Python. I especially want to single out three of its design decisions. First, “the index is not a normal column” leads to no end of special cases. Second, “you can’t index by (row numbers, column names)” completely ignores how people usually indicate rows and columns in a table. Thirdly, its API is insanely large, because once you start method chaining everybody expects their function to become a method, too.

      1. 4

        Yeah I agree – Pandas is not really a killer library for Python, because

        1. it’s not that good compared to tidyverse, and
        2. Plenty of people use Python for other reasons.

        I use Python for almost everything, but I don’t use Pandas.

        It seems to me like NumPy / SciPy / machine learning frameworks attract more people to Python than Pandas.

        On the other hand, I think ggplot2/tidyverse are a major reason that many people use R. (The other reason is that it’s taught in stats courses in colleges.)

        1. 5

          It seems to me like NumPy / SciPy / machine learning frameworks attract more people to Python than Pandas.

          Ya, and you can also add scikit-learn to that list.

          I think ggplot2/tidyverse are a major reason that many people use R. (The other reason is that it’s taught in stats courses in colleges.)

          If you work in statistics, especially for the life and behavioural sciences, there’s a third reason: most new models are published as R packages, as are many domain-specific models and datasets. A quick look at the last four days of CRAN’s recent[ly updated] packages turns up this interesting bunch. (The real list is 10-times as long, so I’ve picked a few raisins from the pudding).

          • OptimaRegion: Confidence Regions for Optima of Response Surfaces
          • fastqcr: Quality Control of Sequencing Data
          • dogesr: Work with the Venetian Doges/Dogaresse Dataset
          • eiPack: Ecological Inference and Higher-Dimension Data Management
          • convergEU: Monitoring Convergence of EU Countries
          • blindreview: Blind Review Using Forward Search Procedures
          • allomr: Removing Allometric Effects of Body Size in Morphological Analysis
          • imputeMulti: Imputation Methods for Multivariate Multinomial Data
          • scMappR: Single Cell Mapper
          • birdscanR: Migration Traffic Rate Calculation Package for ‘Birdscan MR1’ Radars
          • cornet: Elastic Net with Dichotomised Outcomes

          I imagine that for the right specialist, any of these can be a killer library. Or, given that they are used by end users, you could call R a platform and these its killer applications.

      2. 2

        Thirdly, its API is insanely large, because once you start method chaining everybody expects their function to become a method, too.

        A C# language feature, extension methods, solves this. “Extension methods enable you to “add” methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.”

        You can have one type and import whichever set of extension methods for that type. This allows modular and fluent interfaces.

    7. 13

      Rails feels a bit old now, but that’s because it’s mature. I remember the “Build a Blog in 20 Minutes with Ruby on Rails” tutorial being just mind-blowing. I’d been doing PHP for several years at that point. The maturity of Rails’ developer experience was transformative. I proffer that it raised the bar for CLI tooling w.r.t. project initialization. It’s still my go-to for many web projects, although I have a side project’s Rails codebase desperately needing to be upgraded from Ruby 2.6 and Rails 5 to the latest stuff.

      Pandas is a killer framework, but I’m excited by things better than it, like Polars. I’ve moved a couple of projects to Polars, and the code is much cleaner and faster to boot.

      Spark justifies Scala, and so does its parallel collections framework (although other languages have met this bar in since). The first time I dropped a .par into something and watched it “magically” handle the concurrency was wonderfully amazing. At the time, most of my programming experience was in PHP, Ruby, Python, and some other languages where concurrency was not necessarily actually concurrent, or in C or Java where you had to handle so much yourself with a nice foot gun.

      1. 3

        I had a similar experience with Ruby and Rails. I saw the seminal video by DHH and was floored. My prior web experience was with homegrown Perl and with PHP’s Horde project. They worked, but only barely. The productivity increase from those tools to Rails was more than an order of magnitude better… and in 2005 / early 2006, there were no database migrations in activerecord. You still had to manage that on your own.

    8. 11

      Regarding Elixir, Phoenix and OTP have already been mentioned so I want to highlight Ecto and Oban. Ecto provides a very fluent bridge between Elixir and SQL without adding an (often leaky) abstraction over data access and mutation. Oban is built with Ecto and adds a durable background job queue without requiring another piece of infrastructure to manage. Modeling job insertion as a typical SQL transaction where rollbacks work as expected is very powerful.

      1. 1

        I want to add GenServer and Broadway to that list!

    9. 8

      AppKit for Objective-C. When I first used in 2000, it was light-years beyond crappy Java and C++ GUI frameworks I’d used before. (TBH I have to include Interface Builder in this, even though it isn’t a library.)

      1. 5

        It’s still ahead of most ‘modern’ GUI frameworks. The text system in 1992 AppKit is fantastic. Apple didn’t change anything fundamental, they just plugged in some more modern layout engines. It’s a shame UIKit lost most of this and hid it behind the less-flexible CoreText APIs. Some newer frameworks have nicer underlying abstractions but I’ve not seen anything anywhere near as complete as AppKit. If OPENSTEP For Windows had been $100, it would probably have dominated Windows 95 app development.

        Interface Builder is the worst named program NeXT ever released because the name hides the key thing that it does that makes it better than the competition. It is a graphical tool for creating serialised object graphs. This means that you can use it to set up not just the UI but all of the controller and model objects for a stub document and instantiate them with a single line of code.

      2. 3

        On the same note, I learned Java purely because of WebObjects 4.5’s transition from Obj-C. It was similar leaps and bounds ahead of anything else I’d ever seen, and when I got to grips with it I realised EOF was (to me at least) more amazing still, even if EOModeler was a bit cranky.

    10. 7

      sequel, the most complete, flexible and extensible database toolkit / ORM library you can find in any ecosystem. Period. In itself a bigger reason for one to learn ruby, than rails itself.

    11. 7

      For python: requests, beautiful soup, eventlet, keras.

      1. 4

        SQLAlchemy?

    12. 6
      • serde and rayon for Rust, as well as askama and some other lesser-used things.
      • numpy and especially matplotlib for Python, and to a lesser extent scipy and pandas
      • love2d and to a lesser extent TIC-80 for Lua and related langs like Fennel
      • Just the bare OTP for Erlang/Elixir.
      • I seem to have stopped enjoying any other language… 🤔
    13. 6

      The big one for me is libretls which makes writing TLS-aware programs dead simple, and the API makes it easy to export to other languages (I was able to write a Lua wrapper in the time it took me to type it in).

      Another one is LPEG (Lua Parsing Expression Grammar). I think of this as regex on steroids, only it’s an actual parsing module, not regular expressions. You can build up an expression, give it a name, and use that in a much larger expression. You can use it to transform data instead of just returning a string. There’s a lot you can use it for. The neat thing about LPEG is that it itself is a specialized VM geared towards parsing.

      1. 1

        libtls is excellent - note that libtls is the library, libretls is the implementation on top of OpenSSL. There are other implementations in LibreSSL and for BearSSL.

    14. 5
      • Python: seconding numpy and to a lesser extent matplotlib, and requests. Pandas is good because dataframes are great, not because the library is especially well designed :|. Also cffi + hypothesis means I can write property-based tests for any language that can expose an FFI interface. pwntools, scapy + a lot of the stdlib makes it the best choice for infosec/ctfs/reverse engineering.
      • C/C++: Raylib, Raygui, Imgui, SDL2, OpenGL, ffmpeg (you can’t do much multimedia programming without at least touching some of this stuff)
      • Zig: @cImport + all the C libraries out there. Technically not a library, but it enables the use of all the killer libraries in C with a more modern language without having to write FFI bindings by hand.
      1. 2

        Pandas is good because dataframes are great, not because the library is especially well designed :|.

        That’s a very good way to put it. Gives neither too much nor too little credit to Pandas, better than my comments elsethread.

    15. 5

      Svelte changed frontend development for me. Pretty hard for me to imagine it in another ecosystem.

    16. 5

      LLVM for C++. Like Qt, it has bindings in other languages.

    17. 5

      A lot of audio software is written around JUCE and therefore C++.

    18. 5

      Pattern matching (in any language)

    19. 5

      The DOM API for JavaScript. Unfortunately.

    20. 4

      It’s not a library, but I would argue that regular expressions (and text-manipulation built-ins like while <> { # Do things with $_ } ) were this for Perl when I first learned it. (To be clear, lots of programming languages provided regular expressions and ample support for text manipulation, but Perl seemed to do both best and most thoroughly to many people.)

      Earlier, though, I suppose that CGI was Perl’s killer library.

    21. 4

      Spacy for NLP in Python. I haven’t come across anything even remotely as powerful with as easy to use API in any other language

    22. 4

      SQLAlchemy for Python. Probably the most expressive, most flexible features of all SQL toolkits possible specifically because of Python language features.

    23. 4

      3D graphics data structures in C++

      • openvdb
      • GCAL
      • libigl
      • libfive

      LIbraries like this require a ton of expertise and mathematical sophistication. Quality of implementation matters. They are also extremely niche. All the good stuff is written in C++.

    24. 4

      LINQ for C# (coupled with EF or some other ORM).

      I prefer the fluent API over the query. It’s pretty easy to pick up and it becomes second nature to map the entities to a query. There are a few weird things that don’t map well, but overall it’s easy to get type safe queries for SQL.

    25. 4

      dear-imgui might fit. That gives you an incredible simply way to drop in a GUI for debugging into any application that has some kind of realtime loop.

      1. 1

        100% this is one of the first things that came to mind for me! Super fast way to add a UI to an application, and it’s got bindings to pretty much any language. Great library!

    26. 4

      Django for python. You can create so much things with it, and the doc is fabulous.

      1. 6

        Absolutely. Also for a socio-techical reason: Django pushes its users to implement each project component as a self-contained package/directory, and that (a) is a gift to yourself and your team, especially as your application becomes more complex; and (b) has created a thriving ecosystem of actually reusable components.

    27. 14
      1. 4

        I’m curious about this one as it seems super niche. Why is this a “killer” library?

        1. 7

          Hans Reiser is a software developer who developed ReiserFS, and killed his wife. This event is also notable for generating the greatest final blog post yet known: https://freereiser.wordpress.com/

          1. 2

            Yeah I know what and who he is, but was too fixated on the other interpretation of the phrase “killer library” ;)

          1. 4

            Oh haha, whoosh 😂

    28. 3

      For me, PEGs are/were a killer library for Janet.

      1. 1

        What do you typically use PEGs for?

        1. 3

          I used them a lot in Advent of code stuff, or for various parsey tasks. The neat thing about them is that most PEGs are close to how you’d write an EBNF grammar, so you can often transliterate specs to get a half-decent parers for a data format.

    29. 3

      lens for Haskell

    30. 3
      • Java Standard Library
      • Akka for Java/Scala
      1. 1

        What do you find compelling about Akka? Some higher ups have begun talking about using it at $WORK so I’m curious what the appeal is.

    31. 2

      I used to work on vision software. OpenCV and VTK (visualization toolkit) were essential to that work at that time and totally justified using C++. Interestingly, CMake came out of that world and is now pretty widely used as a C++ build tool (although it’s a bit of a beast to learn and probably outdated now).

      Currently I use Go, mostly because of excellent gRPC support. Other languages have gRPC but the quality varies widely and often you run across strange bugs in other languages.

      Python has so many excellent numerics/data libraries, it’s hard to count them all. Pandas gives you statistics, numpy/scipy give you many common Matlab-like algorithms, and Tensorflow/PyTorch give you the absolute best machine learning toolkits out there. It’s kind of funny that a language developed for lightweight scripting and web dev has turned into an absolutely dominant language for math, but here we are.

    32. 2

      Big web frameworks are questionable in this regard, because at this point, all languages have them:

      • Laravel for PHP
      • Ruby on Rails for ruby
      • actix for Rust
      • Phoenix for Elixir
      • Martini/gorilla for Go

      so I don’t think that choosing a language for a web framework is justified nowadays. You just pick the language and it will have a big web framework.

    33. 2

      ecto for elixir - very flexible database interface, in tandem with phoenix it makes much more sense for me and also allows to go against the framework if my use case needs it, comparing to rails.

    34. 2

      I’m tickled to see no JavaScript in the comments, solidifying my conviction that we’re mostly stuck with it because we didn’t have a choice. But I think making Node off of open-source V8 (is this a library or a runtime) in quick succession with npm, Mongo, and Express were “killer library”-level in making JS go beyond the browser.

      I’m always tickled by the “language popularity graphs” circa 2008, where Objective-C just jumped to the top. People must have really, suddenly wanted the memory safety of C with the performance of JavaScript! Of course not, iOS was the “killer app” there.

      As I understand it, Objective-C was the “loser” of a battle for “who could do C with Objects,” which C++ won because Microsoft’s Windows APIs used it. (I suspect I’m answering a different question, on “killer app that justifies language use,” not so much “killer library”…?)

      For my “language of choice,” I’d say the rest of the Elixir answers: BEAM runtime, OTP, Phoenix, Ecto are all sublime. For the opposite: I keep coming back to OCaml for that type system and the speed of generated binaries, but its library situation is whatever the opposite of “killer library” is, so much so I steer most people away from it (usually they’re folks who are starting to get curious about not-boring tech and I hate to throw them the level 4 boss when they’re poking around the tutorial).

    35. 2

      Arguably quickcheck could have been a killer library for Haskell

      I don’t see how quickcheck as a library would be useful. Its main use-case is to be wrapped in a binary an ran once in a while on totally separated bash files (project-wise).

      EDIT: I was thinking about shellcheck :facepalm:

      1. 3

        You’re thinking of ShellCheck. QuickCheck is for Property-Based Testing. :)

        1. 1

          haha! oops sorry, indeed I had this in mind. Now that makes far more sense. Indeed a killer feature that inspired everyone to do the same.

    36. 2
      • Akka for Scala
      • Kafka Streams for Clojure/Scala/maybe Java?
      • I’d say TypeScript alone is a good reason to adopt JavaScript/Node (though Deno might change that)

      Never thought of the go stdlib as a “killer library,” but it’s got to be that: I’d reckon few people move to Go based on the merits of the language itself.

    37. 1

      Animations in Julia. It’s so easy to make cool GIFs & and animated graphs

    38. 1

      Maybe not killer yet but Persism is small and sweet for desktop Java. https://sproket.github.io/Persism/

    39. 1

      A little-known C++ basis library called kj[1, 2, 3]. Packs a ton of functionality in clean, readable code, unlike the std library. Powers Cloudflare workers and presumably maintained by the same team (the original author works there).

      [1] https://github.com/capnproto/capnproto/tree/master/c%2B%2B/src/kj

      [2] https://github.com/capnproto/capnproto/blob/master/kjdoc/index.md

      [3] https://github.com/capnproto/capnproto/blob/master/kjdoc/tour.md

    40. 1

      I really like Raylib for C. Of course, since it’s made in C, it has bindings for a lot of languages.

      Raylib lets me make games with pretty much any language and now it’s my go-to library for testing out new languages.

      However I’m most familiar with it when using C and it has kind of helped me learn a lot about making games.

    41. 1

      If you’re doing any cryptography work, BouncyCastle plus Java (or C#, I suppose). The JVM has pretty broad cryptography support itself as well, plus support for HSMs and smart cards (via the SunPKCS11 provider) has been built in for decades.

    42. 1

      When it was new, GCD/libDispatch for Objective-C. Thinking about queues for concurrency instead of threads was a leap forward. It goes both ways, too: Blocks (closures) and zeroing weak references arrived in the language at or around the same time and the library took immediate advantage. Same goes for Swift today—async functions were everywhere in the platform SDKs as soon as Swift Concurrency arrived. In general, Apple’s primary languages have evolved largely to support SDK improvements. You get the best from each by using the other.

    43. 1
      • tls, lambdasoup (OCaml)
      • sqlite
      • libxml2 (behind swift bindings),