1. 93

I have always been a little skeptical when people overly effuse praise for their favorite language, or tools. I don’t believe everything is perfect, and if someone can identify an area where your favorite tool is lacking then they have a deeper and more nuanced understanding of why they love it.

This is inspired the the “say something nice” thread, but also by a series of blog posts I found a few years ago.

I don’t think it’s unreasonable to be able to name at least 5 things you don’t like/would change about something you love.

So, what is something that you dislike, or would change, about your favorite programming language or tool?

    1. 59

      With Python I dislike everything related to packaging.

      1. 12

        One of my buddies at work was absolutely roasting pip for weeks. He ran across poetry and was like “why isn’t this a first-class citizen like pip is?”

        1. 1

          I’ve had a really good experience with pex - a single executable like a JAR. Worked everytime, except with some fancy python c extensions, but worth a try

        2. 1

          I’ll try that next time! I used pipenv last time I had to work on a python project but didn’t love it.

          1. 1

            I can’t recommend pipenv, it tries to be a wonder-unicorn and ends up being kind of a limping camel.

            IMO if you want managed Python runtime versions for *nix systems pyenv is the way to go, and there are numerous packaging choices out there that improve on the current status quo. I hear good things about poetry.

      2. 4

        +1. On the up side, I think the community knows this is a giant pile of brown stinky stuff, and various solutions have been proposed, but the core team hasn’t had the resources to address this in a whollistic way as of yet.

        There are several levels to this as well - there’s the micro - creating and publishing a package sucks, and the macro - the nature of this suck means that Python programs are unnecessarily difficult to install and deploy in the wild. Again, see above about various community solutions.

      3. 2

        You might find this project (it’s like Rust’s cargo but for Python) interesting: https://github.com/David-OConnor/pyflow

    2. 45

      RustTL;DR: It’s a very impressive language with a clear vision and priorities, but the user interface needs a lot of work. Also, the collection library is much better than Scala’s.

      • Generics with <>. It’s 2017 by now, we know it’s a bad idea. One of the reasons why the language suffers from abominations like the “turbofish” operator ::<>.

      • Strings don’t offer indexing, because it doesn’t make sense for UTF-8. Correct! But Strings offer slicing … WAT?

      • Misuse of [] for indexed access. Having both () and [] doing roughly the same thing, especially since [] can be used to do arbitrary things, doesn’t make sense. Pick one, use the other for generics.

      • Inconsistent naming. str and String, Path and PathBuf etc.

      • :: vs. . is kind of unnecessary.

      • Mandatory semicola, but with some exceptions in arbitrary places: struct Foo; vs. struct Foo {}

      • Arbitrary abbreviations all over the place. It’s 2017, your computer won’t run out of memory just because your compiler’s symbol table stores Buffer instead of Buf.

      • Can someone decide on a casing rule for types, please, instead of mixing lowercase and uppercase names? Some types being “primitive” is an incredibly poor excuse.

      • Also, having both CamelCase and methods_with_underscores?

      • Library stutter: std::option::Option, std::result::Result, std::default::Default

      • iter(), iter_mut(), into_iter() … decide prefix or postfix style and stick with it.

      • Coercions do too many things. For instance, they are the default way to convert i32 to i64, instead of just using methods.

      • Also, converting numbers is still broken. For instance, f32 to i32 might result in either an undefined value or undefined behavior. (Forgotten which one it is.)

      • Bitcasting integers to floats is unsafe, because the bits could be a signaling NaN, causing the CPU to raise an FP exception if not disabled.

      • Forward and backward annotations: #[foo] struct Foo {} vs struct Foo { #![foo] }. Also /// for normal documentation, //! for module level documentation. Documentation already uses Markdown, so maybe just let people drop a markdown file in the module dir? That would make documentation much more accessible when browsing through GitHub repositories.

      • Also, documentation can cause compiler errors … that’s especially fun if you just commented a piece of code for testing/prototyping.

      • Type alias misuse: In e.g. io crate: type Result<T> = Result<T, io::Error> … just call it IoResult.

      • Macros are not very good. They are over-used due to the fact that Rust lacks varargs and abused due to the fact that they require special syntax at call-site (some_macro!()).

      • Pattern matching in macros is also weird. x binds some match to a name in “normal” pattern matching, but matches on a literal “x” in “macro pattern matching”.

      • println! and format! are very disappointing given that they use macros.

      • Compiler errors … ugh. So many things. Pet peeve: “Compilation failed due to 2 errors” … 87 compiler errors printed before that.

      1. 8
        • Library stutter: std::option::Option, std::result::Result, std::default::Default
        • Type alias misuse: In e.g. io crate: type Result<T> = Result<T, io::Error> … just call it IoResult.

        How ya gonna square that circle?

        1. 2

          I think std::io::IoResult would be fine – it would solve the issue of having vastly different Results flying around, while not having single-use namespaces that are only used by one type.

          1. 2

            The general pattern is to import Io instead. When doing this, IoResult would be jarring.

            use std::io;
            fn my_fun() -> io::Result<T> {
      2. 14

        It’s 2017,

        I have some news for you, @soc.

        1. 3

          Haha, good catch. Now you see how old this list is. :-)

          The only thing I got to delete since then was “get rid of extern crate”.

      3. 3

        What’s your preferred alternative to generics with <>?

        1. 6

          [], as it was in Rust before it was changed for “familiarity”.

          Unlike <>, [] has a track of not being horribly broken in every language that tried to use it.

          1. 5

            How is <> broken?

            1. 16

              It complicates parsing due to shift and comparison operators.

              1. 2

                Ah, yeah, that makes sense.

            2. 19

              Pretty much no language has ever managed to parse <> without making the language worse. The flaws are inherent in its design, as a compiler author you can only pick where you place the badness; either:

              • Add additional syntax to disambiguate (like ::<> vs. <> in Rust).
              • Have weird syntax to disambiguate (like instance.<Foo>method(arg1, arg2) in Java).
              • Read a potentially unlimited amount of tokens during parsing, then go back and fix the parse tree (like in C#).
              • etc.

              In comparison, here are the issues with using [] for generics:

              • None.

              For newly created languages (unlike C++, which had to shoehorn templates/generics into the existing C syntax) it’s a completely unnecessary, self-inflicted wound to use <> for generics.

              More words here: Why is [] better than <> for generic types?

              1. 2

                Those are good reasons to not use <>, but as a Haskeller I personally find either style somewhat noisy. I’d rather just write something like Option Int. Parentheses can be used for grouping if needed, just like with ordinary expressions.

                1. 2

                  Haskell feels like it is in the same category as D, they both just kicked the can a tiny bit further down the road:

                  Both need (), except for a limited special-case.

                  1. -1

                    I don’t see how Haskell kicked the can down the road. The unit type is useful in any language. Rust has a unit type () just like Haskell. Scala has it too.

                    I’m not sure what “special case” you are referring to.

                    1. 2

                      The fact that you still need () for grouping types in generics as soon as you leave the realm of toy examples – just as it is in D.

                      (Not sure what’s the comment on the unit type is about…)

                      1. 4

                        Ah, I understand what you’re saying now. But that’s already true for expressions at the value level in most programming languages, so personally I find it cleaner to use the same grouping mechanism for types (which are also a form of expressions). This is especially applicable in dependently typed languages where terms and types are actually part of the same language and can be freely mixed.

                        However, I can also appreciate your argument for languages with a clear syntactic distinction between value expressions and type expressions.

        2. 1

          D’s use of !() works pretty well. It emphasizes that compile-time parameters aren’t all that crazy different than ordinary runtime parameters.

          1. 1

            I prefer my type arguments to be cleanly separated from value arguments (languages that fuse them excepted).

            I find D’s approach slightly ugly, especially the special-cases added to it.

            1. 1

              I prefer my type arguments to be cleanly separated from value arguments

              Well, in D they aren’t type vs value arguments, since you can pass values (and symbol aliases) as compile-time arguments as well. That’s part of why I like it using such similar syntax, since it isn’t as restricted as typical type generics.

              I find D’s approach slightly ugly, especially the special-cases added to it.

              The one special case is you can exclude the parenthesis for a single-token CT argument list and actually I thought I’d hate it when it was first proposed and I voted against it… but now that it is there and I used it, I actually like it a lot.

              Sure does lead to a lot first timer questions on the help forums though… it certainly isn’t like any other language I know of.

      4. 2

        Also, converting numbers is still broken. For instance, f32 to i32 might result in either an undefined value or undefined behavior. (Forgotten which one it is.)

        Yeah, I kind of feel the same way. Even with try_from() dealing with number conversions is a pain in Rust.

      5. 1

        You saved me a lot of typing. 100% agree.

        1. 1

          Thanks! I’d love to know the reason why someone else voted it down as “troll” – not because I’m salty, but because I’m genuinely interested.

      6. 1

        2 pains I have with Rust right now:

        • I would like to be able to connect to a database (Teradata specifically)
        • I want to launch a subprocess with other than the default 3 stdio descriptors (e.g. exec $CMD $FD<>$PIPE in sh)
        1. 2

          I know it’s technically unsafe and that might preclude it from your use, but does CommandExt::pre_exec not fit your bill?

          1. 2

            That could work. I’m still new to Rust so I haven’t fully explored the stdlib.

      7. 1

        Ahahaha, this is a great list. I’m curious about a couple things though, since you’ve obviously put a lot of thought into it…

        Bitcasting integers to floats is unsafe, because the bits could be a signaling NaN, causing the CPU to raise an FP exception if not disabled.

        The docs for f32::from_bits() and such talk about precisely this, but I considering the misdesign of signaling NaN’s really don’t see how it could possibly be made better. Any ideas?

        …They are over-used due to the fact that Rust lacks varargs…

        What little experience I have with programming language design makes me feel like varargs are a hard problem to deal with in a type-safe language, at least if you want to allow different types for the args (instead of, say, forcing them all to be what Rust would call &dyn Display or something). Do you know of any language which does it Right?

        1. 1

          The docs for f32::from_bits() and such talk about precisely this, but I considering the misdesign of signaling NaN’s really don’t see how it could possibly be made better. Any ideas?

          Rust could have disabled the trapping of signaling NaN’s on start up, but I think Rust fell into the same design mistake of C:

          Scared of making the use-case of the 0.01% (people who want signaling NaN’s to trap) harder to achieve, they made life worse for the 99.99%.

          varargs are a hard problem to deal …

          Agreed, it’s safe to say that language designers hate them. :-)

          … at least if you want to allow different types for the args

          I think this is only partially the reason. You can still have only same-typed varargs at runtime, but allow recovering the individual types of the arguments in macro calls – which is exactly the case for format! and friends.

          Do you know of any language which does it Right?

          I think in the case of format strings, focusing on varargs is the wrong approach. If you imagine how you want an ideal API to look like, you probably want to interpolate things directly inside the string, never having to go through the indirection of some vararg method.

          Instead of having the formatting parameters in one place, and the to-be-interpolated values in a different one, like in …

          let carl = "Carl"
          let num = 1.234567;
          format!("{}'s number is {:.*}, rounded a bit", carl, 2, num)
          // -> "Carl's num is 1.23, rounded a bit"

          … wouldn’t it be much nicer to write (this is Scala):

          val carl = "Carl"
          val num = 1.234567
          f"$carl's num is $num%.2f, rounded a bit"
          // -> "Carl's num is 1.23, rounded a bit"
          1. 1

            Julia has nice string interpolation too. I honestly don’t understand why more programming languages don’t have it. Does everyone just forget how useful it is in bash when they come to design their language?

    3. 16

      C++: Gives me so much power to hang myself and decorate my body beautifully. I love and hate you so much.

      Ruby: You were the chosen successor to Smalltalk, but never adapted the VM and the powerful GUI runtime editor. Instead, you eloped and married Rails.

      Rust: So elegant, until I get to an inextricable line, and with the compiler fighting me, that line is as painful and takes about as long as giving birth.

      1. 4

        Rust: So elegant, until I get to an inextricable line, and with the compiler fighting me, that line is as painful and takes about as long as giving birth.

        I feel this in my soul. You can tell the compiler is trying its hardest to tell you what you’re doing wrong, but when you have a lifetime error or an error with a trait object and you’re just not getting it, and you wish there was a “well screw this” button you could press to at least get the damn thing to compile… sigh. The things we do for love.

        1. 3

          and you wish there was a “well screw this” button you could press to at least get the damn thing to compile

          There is: you fall back to reference counting for just that. Then, you get its safety and performance benefits everywhere that borrow checked with just that part costing you. Then, you can figure it out later if it’s worth it.

          1. 4

            99% of my horrible issues revolve around manual lifetime annotations. For some reason, I tend to get along with the borrow checker the vast majority of the time.

            e.g. this line using clap was exceptionally painful:

            type HelpFn<'a> = fn(App<'a, 'a>) -> App<'a, 'a>;

            1. 5

              That looks horrific. I mean, Lisp at least looks more consistent even as it horrifies some newcomers.

              1. 5

                At some point one of my projects acquired a struct member with the following type:

                Option<&'subj mut &'c mut dyn FnMut(&[u8]) -> Option<(Vec<u8>, Vec<u8>)>>

                I don’t know how it happened or when, but there it is.

                1. 3

                  I just realize something. This language might overtake Java in lock-in and job security one day. Just with a smaller number of companies.

                2. 1

                  Yikes. I’m guessing the 'subj lifetime must be guaranteed to outlive the 'c lifetime?

                  1. 3

                    Indeed – the structure it’s defined in is struct Subject<'a: 'd, 'r, 'o, 'd, 'i, 'c: 'subj, 'subj>. Don’t look at me like I meant for this to happen.

            2. 3

              I’ve tried to learn Rust, and lifetimes are where I ran into a brick wall and gave up. The ownership/borrowing concepts and the borrow checker wasn’t a problem.

      2. 2

        Ruby: You were the chosen successor to Smalltalk, but never adapted the VM and the powerful GUI runtime editor. Instead, you eloped and married Rails.

        Sooo much good snark potential wasted there! You left out the usual rant about glyphs and the perl syntactic sugar!

        1. 3

          Glyphs? You mean sigils? I don’t have a problem with those because they indicate scope and not types (like last time I used Perl was years ago), which is useful for making methods with same name as instance variables (@). I’d agree with complaints about the obliquely named global variables like $$. I actually prefer Ruby over Python when dealing with lots of regular expressions because it’s a language built-in.

          The VM + GUI runtime editor (like Smalltalk had) I mention I feel is a massive missed opportunity because of Ruby’s incredible runtime introspection, metaprogramming capabilities, and dynamic reload. It would have helped better distinguish itself from Python with these strengths, but I feel like the train is passed on that though.

          1. 1

            The VM + GUI runtime editor (like Smalltalk had) I mention I feel is a massive missed opportunity because of Ruby’s incredible runtime introspection, metaprogramming capabilities, and dynamic reload. It would have helped better distinguish itself from Python with these strengths, but I feel like the train is passed on that though.

            I agree. Even things like RubyMine don’t go nearly far enough. You could still build one! Although tbh ruby has the same problem Python does in that there aren’t very many superb cross platform GUI environments for it, so you’d need to design one of those too (Not that every Smalltalk implementation didn’t do that too :)

    4. 16

      Clojure: Its inconsistent treatment of nil remains hands-down the most common source of errors for the codebases I work on. Also tends to be a memory hog. Also bizarrely omits pattern matching from the core language, which is very silly.

      Fennel: Shame that the runtime doesn’t support concurrency. The zero-overhead design goal means that it can’t add creature comforts like map/filter/etc; you need to bring in a library for that.

      Elisp: Having functions split out into the lisp-2 namespace makes higher-order functions usually too tedious to be worth it. The built-in functions are inconsistently named with weird historical quirks.

      Racket: No matter what the question is, the answer will always be “write a macro”.

      1. 6

        Just wanted to pop in and say thanks for Leiningen :D

      2. 2

        Racket: No matter what the question is, the answer will always be “write a macro”.

        Hello, I’m a relatively new Racketeer here and I’ve yet to find a good reason to write a macro and in fact, have read a section of a delightful blog post that suggested reasons to prefer core Racket library functions over macros.

        In your opinion, how and why are macros the answer to questions with Racket? Thanks :)

        1. 2

          There are a lot of things which can be done at runtime in other lisps which have to be done at compile-time for Racket. For instance, Racket wants you to use structs instead of hash tables, which means you can’t do things like deeply nested updates with arbitrary field names; the fields need to be known at compile time. (Lenses help a lot with this, but Lenses don’t ship with Racket; they’re a 3rd-party library) For the most part it’s worth the trade-off to not have to deal with nil and for the errors it catches, but it’s awkward coming from languages that offer more flexibility. (I wrote a bit about it at https://technomancy.us/185)

    5. 14

      Rust: I wish there was an alternate version of the language that was garbage collected. It would simplify so much.

      Python: In a language that values simplicity, the distinction between attributes and dictionary members always seemed weird and unnecessary to me. Every time I need to look up this, I think that something went wrong somewhere.

      Langs I’ve used much less but still mostly appreciate:

      F#: There seems to be a slight consensus in the community that Results shouldn’t be used, and that you’re always going to be dealing with Exceptions, so get used to it. I think that’s backwards. If I’m working with an ML-style lang, I want error handling to be explicit.

      Go: the nil interface problem is a bug. You cannot change my mind about this. The FAQ literally starts with “Under the covers”– you need to know an implementation detail in order to use interfaces properly. Seriously?

      1. 4

        Rust: I wish there was an alternate version of the language that was garbage collected. It would simplify so much.

        There is, it’s called OCaml

        1. 3

          The parallelism story in OCaml leaves me sad, though.

          1. 1

            What’s the OCaml response to this usually, spin up more processes?

            1. 2

              I’m not entirely sure. I know there’s Monadic async io libraries, but the runtime still has a GIL. There’s work being done to bring multicore with parallel garbage collection to it, but that’s not “ready for prod” yet.

        2. 2

          I want OCaml with Rust’s package-manager, then.

      2. 3

        In my experience there are primarily people who pretend there is a consensus. I think the division is highly dependent on how much you interop with C# and in what way you interop. We wouldn’t have a result type if there was a consensus to use exceptions, after all it wasn’t added that long ago. If you are consuming a lot of C# code, you’ll be getting exceptions from it and will need to handle them. If you’re writing primarily F# you genuinely could just use results.

        Personally I just use result types, and if there is an exception I let it crash . This isn’t a good idea for many people but it’s fine for me because I don’t really get exceptions except when I should halt and catch fire.

        1. 1

          I’m happy to hear this! Maybe I’ll have to reexamine F# for my next project.

      3. 2

        the distinction between attributes and dictionary members always seemed weird and unnecessary to me.

        I’ve been thinking about this for the Oil language, which is heavily influenced by Python.

        The problem is that dictionary attributes come from user data, i.e. from JSON, while methods like .keys() come from the interpreter, and Python allows you to provide user-defined methods like mydict.mymethod() too.

        Mixing all of those things in the same namespace seems like a bad idea.

        In Oil I might do introduce an -> operator, so d->mykey is a shortcut for d['mykey'].

        d.keys(), d.values(), d.items()  # methods

        Maybe you could disallow user-defined attributes on dictionaries, and make them free:

        keys(d), values(d), items(d)
        d.mykey  # The whole namespace is available for users

        However I don’t like that this makes dictionaries a special case. Thoughts?

        1. 2

          I like the idea in general, and I’ve wanted a dict access shortcut in python for a while.

          But I think what I just wind up falling back to is my mental model of javascript. It’s at least simple to reason about. And heck, I don’t even like javascript in general.

    6. 12

      Idris: the main premise is interactive development in the sense that the compiler can add definitions, split a pattern variable in cases, print out the type into of variables in some context (and this info is way more useful than in other languages), and autocomplete holes. There are functions that literally write themselves out of their type.

      The major issue is that compilation times get insane. This process works when you start a new file, or use only basic constructs, but as soon as you start using overloaded >>= a lot it can take minutes, essentially removing one of the biggest conveniences of this language.

      Another major practical issue is that error messages get broken easily, i.e. unable to find (an often simple) root cause when you’re in some monadic context and need to lift/call.

    7. 11

      Rust: I feel that sometimes I have to hit the wall with my head a few times before I’ll figure out how to write this specific line, which simply moves data from point A to point B. Also sometimes the fact that I need to change the architecture of the program is quite discouraging, because writing in Rust has a high-energy cost, but maybe it’s because I’m not very fluent in it.

      Ruby: Documentation simply sucks and sometimes it’s a joke. Sometimes I wonder how Ruby was ever a thing with documentation like that. Very often I need to puts() a variable to the screen to know its type, because docs doesn’t say anything about it.

      Scala: Why sbt was created anyway? In order to torture new Scala programmers? I’ve spent a long time before I’ve found a clean solution how to write Scala code under Vim (sbt-client) without waiting 10 seconds for clean build to finish.

      Java: InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState. Also, JVM is a behemoth; it eats RAM like a hungry cat at 5:30 AM, and its startup time discourages writing any CLI tools in it. Plus, it’s a TOP-1 language on TIOBE Index, and it doesn’t have any decent GUI frameworks for writing desktop apps.

      C++: I have a real love-hate relationship with this one. It’s 2019, let’s add a filesystem support to standard library! Then, introduction of auto made things more modern. But, let’s get quickly back to 1970’s because I need to #include a file. Only 5 people on earth have created a full C++ parser, which will get outdated in 3 years because a new standard will come up. C++ still doesn’t have library management (there are contenders: nuget, conan, but still nothing standard).

      1. 2

        doesn’t have any decent GUI frameworks for writing desktop apps.

        It has quite a bit in the way of GUI frameworks, but I agree that they can be a pain. JetBrains seems to have made it work with their IDEs so that they feel powerful and useful despite not being a native app.

        1. 2


          Holy shit that’s actually real. Is this a machine-generated class or something? Surely no human named that?!

          1. 17

            I think it’s poetic.

            Internal Frame, Internal Frame /
            Title Pane, Internal Frame /
            Title Pane, Maximize Button, Window Not Focused /
            1. 6

              (sing to the tune of Triangle Man by They Might Be Giants)

              1. 1

                Didn’t scan

        2. 2

          Well, I was a little bit harsh with the decent term, because it’s true that Swing is decent, I just wish there were bindings for Qt or Gtk+ (i.e. reactivation of QtJambi).

    8. 11

      C: treating a leading zero in an integer literal as an octal signifier is just a spectacularly bad idea (a mistake sadly replicated by countless other languages). Also, the fact that int* x, y declares one pointer-to-int and one int is utterly nonsensical.

      Python: lack of type checking is painful; the fact that they added type annotations but made them semantically equivalent to comments just adds insult to injury. Also, the enum bodge, ugh – why even bother?

      Bash: while set -e is still helpful enough that I typically use it in scripts, it has many sharp edges and totally unexpected quirks.

      1. 5

        Also, the fact that int* x, y declares one pointer-to-int and one int is utterly nonsensical.

        Just because you’re writing it wrong. int *x, y makes complete sense.

        I agree that it would’ve been nicer if the whole type was on the left side and the variable name on the right side. int*[] foo and int[]* foo are both unambiguous (array of pointer to int vs pointer to array of int), while int *foo[] is ambiguous, and complex function pointer typedefs can make it hard to even find where the name of the definition is. However, there’s no need to make it more confusing than it already is by writing your declarations as if the “pointer-ness” belongs to the type name rather than the variable name.

        1. 3

          Except that’s exactly the point of writing it that way – pointerness is part of the type, and belongs with it. I’m aware that putting the asterisk with the variable name is common practice, but that doesn’t make it any less screwy – it’s basically a convention that’s developed to accommodate a misdesign in the language. A better syntax wouldn’t accept that arrangement at all, and would require the asterisk to be with the type instead of the variable.

          1. 4

            For better or worse, C generally has a policy that the definition of a thing should look like a use of a thing. If you want to use x as an integer, you define it with int x. If you want to use *y as an integer, you define it with int *y. If you call a function as foo(a, b), the definition looks like int foo(a, b)… or at least it did in K&R C, until the folks at ANSI changed things.

            Writing int *y is not a convention developed to accomodate a misdesign in the language, it is the misdesign in the language.

            1. 2

              int *y is certainly a convention; the language doesn’t require it, and int* y is just as valid, it’s just less conventional. And, setting aside my awkward “developed” wording (inadvertently implying it was some post-hoc reaction), it’s also clearly a convention that accommodates (as in, aligns with) the misdesign.

          2. 1

            Whether it’s a pointer or not is certainly part of the type. So is whether it’s an array or not. However, syntactically, they’re both associated with the variable name, not the type name.

            As I said, I agree with you. Had I designed the language now, I would have made the syntax just be the complete type on the left, and the name on the right. I just don’t think that insisting on writing C as if its syntax worked differently is very productive, because it leads to the confusion you correctly pointed out.

    9. 9

      I love Clojure, but we need to have a talk. Specifically, the use of nil-punning is pretty terrible. (if (seq xs) ...) is not actually a good way to spell (if (not (empty? xs)) ...) but both the language designers and the community embrace this conflation of null and the empty sequence. There are other issues around nil and collections as well.

      Kotlin is my current favorite, but it’s not OK that the best way to write Kotlin currently involves a behemoth of an editor that takes like 2 GB of memory. I can write Clojure in Emacs, which only takes about 80 MB.

      1. 1

        I haven’t tried it, but you should take a look at the Kotlin language server.

        1. 2

          It looks promising, and I love the idea of a language server, but I failed to get it working in my alotted yak-shaving time. I need to take another crack at it. :-)

    10. 9

      Ruby: Mixins can make you feel like you are improving code, but I’ve seen some Ruby classes with a dozen modules included. It makes the class “smaller”, but adds a ton of misdirection. It leads to situations where 1000 lines of interrelated code are split over 100 files.

    11. 9

      6809 assembly: Why do you not allow indirect indexed with direct addressing? You allow JSR [$A002] but not JSR [$02] (8-bit address; DP provides high address). That hurts.

      x86 real mode assembly: Segment registers. I know why you have them, but I don’t like them.

      x86 32/64 bit assembly: Stop. You are already the King of CISC. You can’t stop adding new instructions now. Having a 1000+ page tome describing your instruction set is silly.

      Forth: stack noise words like DUP, ROT and DROP litter the codebase.

      C: Undefined behavior lurks everywhere. One comment for the standard committee—stop pretending the world hasn’t standardized on 2’s complement with wraparound. That’ll force compiler writers to stop pretending it doesn’t exist and break working code.

      Lua: You are missing += (and others, like *=, etc). This isn’t bad for things like i = i + 1 but it is seriously annoying for things like a_long_name[another_name] = a_long_name[another_name] + 1.

      1. 1

        Forth: stack noise words like DUP, ROT and DROP litter the codebase.

        How else would you do it?

        1. 1

          There’s the LOCAL wordset in ANS Forth, but it’s an extension, not a core feature. Short of that, one just has to learn to live with stack noise words.

          1. 1

            Very glad locals aren’t CORE, the complication is totally unnecessary.

    12. 8

      Go: I dislike how much it penalizes you for trying to hide your data. Especially when it comes to dealing with reflection based serialization. This is a deep consequence of how the language is designed and is a trade off from other features of the language, like default values for all types, which have their own benefits.

      Rust: My biggest gripe at the moment is probably compilation times, but that one is kind of boring. I have other boring gripes. I think my most interesting gripe is the lack of GATs, or otherwise some mechanism to permit iterators (or an equivalent) to return a borrow to its internal state. It prevents a lot of interesting performance related patterns. Thankfully, it appears to be on the path to fully supported async/await, so I am hopeful. But at the same time, I lament the additional language complexity that it will bring.

      1. 2

        Rust … GATs … additional language complexity

        I’m terrified of the complexity GATs will likely bring into the language. I have a nightmare that it will be the door-to-hell that enables Rust to continue messing up things that have caused significant ecosystem trauma in the Scala community.

        1. 3

          Indeed. If it were just my pet use case, I’m not sure it would be worth it, even though I personally would still want it. But I think it being on the critical path for astnc I/O probably unfortunately tips the scales.

    13. 7

      On some level, I love C. It’s really easy to write C programs with massive security holes because something you done ended up being undefined behavior. It’s not really doing its job as cross platform assembly with syntax sugar, not is it a nice modern high level language.

      I love Python. I wish the python 2 vs python 3 bollocks was over already, and I wish doing a sync stuff with it was as nice as with JavaScript and promises (including the community support and adoption JS promises has).

      I love Go, but I generally prefer when packages are identified through their names rather than their github URLs. Also, GOPATH can go fuck itself. In general though, it’s an awesome language in-between dynamic scripting languages and C.

      I love Rust on a conceptual level, but its syntax is intimidating and I haven’t taken the time to learn it.

      I like C++, but the standard library and other libraries rely wayyyy too much on template magic, to the point where error messages become megabytes of incomprehensible compiler soup. There’s also so many ways to do things, each with its own really subtle but important set of pros and cons, that it’s easy to feel paralyzed by choice. Also, compile times are truly ridiculous, with whole-day compile times not being unusual for the bigger C++ projects out there.

      I love JavaScript, especially after ES6, but I hate the community. Any serious JavaScript project ends up with thousands of dependencies, and as someone who feels like I’m responsible for my whole project, not just the code I personally wrote, that’s a hell of a burden.

      1. 1

        I love Python. […] I wish doing a sync stuff with it was as nice as with JavaScript and promises

        You may be aware of this already, but if you like promises in JS you’ll probably love async / await. (I know they’re syntactic sugar for promises - but what sweet readability sugar!)

        1. 1

          Yeah, I know Python has asyncio and async/await now, that’s why I threw in the part about community support and adoption; chances are that with JavaScript, I’m going to use a library which already uses promises, or it uses callback which is super easy to work with alongside promises. Most Python libraries I use just blocks and expects you to use threads, and using that alongside python’s async/await is much harder.

          (Also, unrelated, but I just noticed that iOS’ autocorrect corrected “async” to “a sync”. I’m getting fairly good at spotting when it’s incorrectly replacing “its” with “it’s”, I suppose I’ll just have to be careful when writing “async” too.)

    14. 7

      C: Look, man, I really love ya, but can you maybe chill with the undefined behavior? Right now, your choice is between under-defined signed integers and unsigned integers. Can you maybe give us a portable way to clear sensitive memory? While you’re at it, remind me why I can’t assume uint8_t is an unsigned char if uint8_t exists; can we do something about those aliasing rules there? Devil’s in the details, and it’d be nice if we could maybe figure out those details and ignore one’s complement and 36-bit integers for a second.

      Perl: I think at this point it’s too late, but function calls with & will never sit right with me.

      PHP: Your documentation is generally useful, but I do wish that you’d hoist user comments into the “main” documentation on occasion.

      Ruby: Please stop iterating so quickly. A release every couple of years is fine. You’re making life hard on Debian when everyone jumps on the new feature bandwagon. And please get your documentation in order; the OpenSSL module is basically “fiddle in irb until it works) land permanently, and separating out half of the core modules (but not all) onto a separate website keeps getting me.

      Java: I hope you were listening for the “iterating so quickly” part I just told Ruby. Because you’ve now started going down that dark path, too. Lambdas just broke the paradigm, too, so now it just feels kind of alien at times.

      C# (or .NET in general, I suppose): I want to like you, but your portability is just not quite there yet if I want to use you on OpenBSD. Maybe someday.

      Literally everything that isn’t C or C++: Operating systems have package managers for a reason. Please at least try and cooperate with them. I’ve already given up that idea that you write some kind of man-compatible format like Perl does.

      1. 9

        Literally everything that isn’t C or C++: Operating systems have package managers for a reason. Please at least try and cooperate with them. I’ve already given up that idea that you write some kind of man-compatible format like Perl does.

        As a distro maintainer, this sentiment needs to be shouted from every rooftop by every programmer.

        As a programmer, even before I was a distro maintainer I hated that each language felt that it was so special that it couldn’t possibly be managed with apt or yum or emerge. Give me a break.

        1. 4

          Thanks for your work maintaining a distro! Fantastic, important work.

          Some perspective from the other side of the fence:

          I’d need to support apt, and yum, and emerge, and also something custom for the windows folk, and something else custom for the mac folk.

          Since lots of people need something custom anyways, I’ll need to build a custom thing.

          Asking me to also support multiple package managers, none of which I’ve ever needed to be familiar with before, is something of a non-starter. I already have this custom thing I built; not only does it work OK, I already understand it and can offer support to people who get stuck.

          1. 2

            No, you need to request the distros to package your work and let them actually do it.

            Can’t speak to Debian, but Fedora, Gentoo, Adélie, FreeBSD, etc all have very active communities (especially around languages such as Python or Ruby or JS) that get excited about packaging new things.

            This doesn’t even have to be “reaching out to distros personally”, it could be a python-pkg-announce mailing list et al that interested parties subscribe to, and discover new packages being released (at least in beta, but hopefully actually 1.0s) that should be considered for packaging.

            However, this also goes to the “npm disease” of having ten thousand (or more) deps for each major package because common wisdom in some of these circles seems to be you should be using packages for everything, even things that will never change, because ~ code reuse ~. So it’s also about balance.

            These problems are solvable, IMO, but it’s going to require more distros (ala Debian) to be more accepting of new packages, and it’s going to require authors writing packages for these languages to stop having what I termed (perhaps unfairly) npm disease.

            edit: I should also note that I do appreciate that this sort of thing would require a lot of coordination from people that historically do not want to coordinate, and it will probably never happen – what I described is something close to (but not) ideal.

            1. 1

              I still have to implement a custom thing (eg rubygems) for the significant number of users who don’t have a package manager available.

              Given I’ve built a custom, language-specific dependency manager - why would I not use it across-the-board?

      2. 1

        Perl: I think at this point it’s too late, but function calls with & will never sit right with me.

        Heh. I used to declare my subs before calling them (I believe I got that habit from Pascal) and later when I decided I liked my function declaration lexically after my main code, I simply started to pre-declare them.

        Today, researching this, I gather from perlsub that as long as I pass my arguments as a list I don’t even have to pre-declare them…

    15. 7

      Python: The major additions to the language in the last few point releases feel like they’ve expanded the surface area of the language significantly, without really adding useful affordances that would change what I use Python for in the first place. It no longer feels like the calm, simple language I fell in love with.

      Clojure: Close JVM integration is a crutch as much as it’s a boon. It’s hard for me to feel like I’m working in a real, self-contained language when things like the backing Java class for a map instance are leaking all over the place in stacktraces. I don’t see how nil punning provides any value at all.

      Rust: This is petty, but I’d like a better IDE experience than is currently available. A GUI library without extensive caveats would be nice.

      Haskell: The documentation culture is extraordinarily offputting in its terseness (especially for modules providing less-publicized abstractions like e.g. bifunctors).

    16. 6

      Python: I wish asyncio never happened.

      Racket: I wish the compiler was faster and that it had better error reporting.

      Despite having used many other languages, I don’t think I can say I love any of them as much as these two.

      1. 2

        Python: I wish asyncio never happened.

        Just ignore it? Seriously - unless you have use for it, it’s in no way a required part of the standard language?

      2. 1

        What is it about asyncio you dislike so much?

        1. 5

          I wrote about it on HN a while back: https://news.ycombinator.com/item?id=18110319

    17. 6

      Python: The GIL ruins lots of things. https://realpython.com/python-gil/ :(

      1. 4

        It ruins a lot less than people think it does, and there are a lot of cases where sub-interpreters will help here.

        What’s your specific use case?

    18. 6

      Common Lisp

      Backquote doesn’t guarantee the resulting list structure is freshly consed, so using it as a quick way for “templating” lists isn’t completely safe.

      There’s no reliable way to determine whether a stream is a binary or character stream, i.e. no way to answer “can I call read-byte on this thing without it erroring?”. There’s no binary-stream-p or character-stream-p, and stream-element-type doesn’t work for bivalent streams:

      (subtypep (stream-element-type *standard-output*) 'character)
      (write-byte 64 *standard-output*)

      You just have to try calling read-byte or write-byte and see whether it errors, but of course that has side effects. Welp.

      read-line always returns a string, even if you’re reading from a stream with :element-type 'base-char. So if you want to read lines and get back simple-base-strings for performance (e.g. when reading data from 20gb FASTQ files), you have to implement that yourself. (I’m not 100% sure the standard requires this — maybe this is something an implementation could actually do.)

      peek-char and unread-char are nice, but there’s no peek-byte and unread-byte.

      Pathnames are bonkers.

      If you want to write a nicely-behaved iteration macro in pure CL you really have to use do as a base, not loop (and do is annoying to use). Otherwise if a user uses your iteration macro inside a loop and calls loop-finish it won’t do what they expect.

      Allowing defmethod without a corresponding defgeneric makes it way too easy to typo the method name in various ways and spend 15 minutes wondering why the hell your method isn’t getting called.

      (subtypep 'real 'complex) ;=> NIL

      Package local nicknames aren’t part of the standard. This is improving though — most of the major implementations support them and there’s a trivial-package-local-nicknames library now.

      There’s no custom hash functions for hash tables in the standard (there’s a portability library for it though).

      There’s a lot of fancy features on arrays that not very many people use (e.g. displacement) that make it harder for implementations to optimize them unless you do everything with simple-arrays. Removing some of the lesser used features would let implementations do more.

      Performance profiling is not great. SBCL’s statistical profiler is alright, but not as nice as something like the profiler in jvisualvm.

      There’s probably more, but that’s all I can think of off the top of my head.

    19. 5

      SQL is hardly one language but a fragmentation of competing languages (PL/pgSQL, T-SQL, PL/SQL, ..?) that will likely never converge. Handling of nulls can surprise you in bad ways (not in !) that might take you a long time to figure out (no errors, no signs). Migrations & packaging are kinda DIY / leave-it-to-the-framework. SQL really deserves a standard lodash library: there’s still no pad-left in T-SQL, and group_concat/string_agg took ages to ship.

      1. 1

        Yes, Looker’s LookML sounds great, but I can’t find much in the way of overviews or documentation that isn’t wrapped up in instructions specific to their platform, or a sales pitch.

    20. 5

      Haskell: the syntax. Yes, it’s as basic a complaint as they come, and I shouldn’t care. I even like minimal syntax in general. But it always seems to turn into operator soup. Also, I dread having to compose monads.

      Python: type annotations are almost completely useless. async fn is hacky and feels like a shark-jumping moment. I despise refactoring a system in Python, which always happens. The GIL.

      Ruby: the ecosystem was taken over by Rails, which encourages unnecessarily tight coupling in a dynamic language, making refactors/upgrades awful.

      C++: header files are a burden to keep in-sync with the corresponding implementation file. std::move() is utterly laughable when compared against Rust (“the moved value is considered to be in an invalid state and should not be accessed”). The whole trend of deferring runtime type checking to libraries like Boost.Any mixes slow compile times, templates, and runtime type-checking to achieve the worst of all worlds.

      Kotlin: the right ideas in a language without the conviction to go all-in on them, resulting in a proliferation of syntax.

      Rust: compile times. Lack of native UI libraries on most platforms (excepting GTK), and, more importantly, this isn’t seen as a problem due to the existence of WebViews.

      1. 4

        async fn is hacky and feels like a shark-jumping moment.

        Having gone away from Python and then come back to it, I kinda feel this way, too. It’s so annoying and difficult to work with. And since JS-land is where I’ve been the last few years the contrast with JS’ “just works” async and await is even more stark.

      2. 2

        Kotlin: the right ideas in a language without the conviction to go all-in on them, resulting in a proliferation of syntax.

        This. It’s so sad that they stole half of Scala’s good parts, but then got scared and made things worse again. (See companion objects, properties, <>, extension methods, etc.)

    21. 5

      Objective-C: the memory management story really suffered, Cocoa was horribly documented, and of course now it’s on the scrap heap of history.

      OCaml: the Batteries/core split is really maddening.

      Haskell: I am not entirely in love with Haskell, but I do like it a great deal, but man alive, it can turn into operator soup pretty damn quickly, and the packaging/build story is … deeply, tragically suboptimal. No, I do not want to use nix, thanks.

      SQL: It’s not closed under composition, so you have horrible workarounds to do basic declarative things; it’s a 4GL mess of syntax; it gets the ordering of projection and selection completely backwards; and the NULL treatment is an abomination.

      1. 1

        the memory management story really suffered

        What do you mean?

        1. 1

          Garbage collection should have never been introduced; it was a bad digression.

          1. 1

            Ah, right. I wasn’t around for that era, I first got into Mac/iOS dev when Mountain Lion was released. You happen to know of any blog posts / articles that accurately depict that arc? No pun intended ;)

    22. 4

      Erlang has some confusing error messages.

      1. 2

        On the other hand, error messages are in the same language as the code. So while it looks intimidating, one can get used to it, or, even better, write logger handler that will translate it to “more human” representation at will.

    23. 4

      C++: My true love, but even two full decades after C++98 you are still trying to absolve the sin of template overuse. And you really, truly need something better than #include (I personally love #import from Obj-C, maybe look in to that). You finally have first-class lambdas, so I have hope for you yet.

      Python: Giving everyone so long to stick with Python 2 has ruined the ecosystem for me. Even as recently as two months ago, I still had issues getting upstreams to accept patches to let their code run on either 2 or 3 because they were so afraid of losing compat with 2. You should have killed Py2 off when 3.3 came out and forced everyone to use the objectively better 3. Also, pip still has silly bugs that are unfixed after years. I’ve lost a lot of my hope for you, but maybe in a few years when 2 is Really Truly Dead things will look better.

      C♯: You’re a beautiful, modern language that is basically unusable on any usable, modern platform (Linux, OS X, *BSD) primarily because there are three warring .NET implementations (Mono, .NET Core, CrossNet) instead of the community just committing to one and making it run really well.

    24. 4

      F#: Despite being an “ML” the metaprogramming functionality leaves a lot to be desired. Code quotations really aren’t all they should be for example. Type providers can’t generate F# types.

    25. 4

      Rust: Refactoring something is a pain in the ass. For example, if I’m working on writing a new struct and think I need a lifetime as part of the struct, I have to add two annotations to each impl block where the lifetime is used. Although this has gotten better with lifetime elision, it’s still just a lot of boilerplate that makes it incredibly frustrating to deal with.

      Python: Please let me have static types, I’m begging you (mypy is getting there, but it’s not perfect)

      C++: After writing Rust, and then trying to move something without using std::move, 1000 lines of error messages leaves me aghast at how people are able to be productive in this language. I realize that you begin to see patterns in these error messages (e.g. trying to copy something without a copy constructor) and if you really take the time to read a handful of error messages, you can usually pinpoint what’s going wrong. But it’s a lot of work.

      1. 2

        If you want types with Python… have you looked at Nim?

    26. 4

      Scheme: car and cdr just upset me; who cares how they were originally implemented on the IBM 704? Also, as much as part of the reason I love Scheme is how small it is, there are some very blaring omissions that virtually every implementation adds as (incompatible) extensions, and that is pretty silly.

    27. 4

      Python : I loathe the choice of the + operator for string concatenation. I looks ridiculous to me. Addition is commutative, and string concatenation is the least commutative thing there is. Any other operator would be a much better choice than plus: the product, a dot, a space, whatever. Heck, even the minus operator (which looks like a hyphen) would be better than plus. I force myself to use string formatting in convoluted ways just to avoid the concatenation operator; it just freezes my blood when I see it.

    28. 4

      Elixir: Sometimes it rely too much on sugar. For example why make :console logger backend “special” when it could be used in the same way as any other Logger.Backends.Console?

      Rust: I still dislike how ? works. <> choice for generics.

      SQL: It should be QUEL that won. The SQL syntax is pretty much inconvenient.

    29. 4

      JavaScript: class syntax obscures the prototypal inheritance behind the scenes, causing confusion particularly for folks who are not familiar with it. Promises are nice but their syntax is not great if you’re not using async/await. There is probably too much syntactic sugar going into the language right now.

      Lua: 1-based indexing. I always forget. Also, weird syntax like -- for comments or .. for concatenating strings.

      Python: Py2 is still a thing and still confuses folks. I really, really, really hate writing self as the first param in methods.

    30. 4

      LuaJIT: Mike Pall abandoned it and nobody else can understand the codebase :(

    31. 3

      Python 3: Making the REPL harder to use. To give one example: That dict.keys() returns a dict_keys instead of a simple list like it used to. I’m sure there’s a good reason, but it still drives me crazy. The REPL (and Notebook) experience is what keeps me coming back to Python. Otherwise I’ll just write Typescript.

      Edit: Here’s another one. map has become lazy, and now returns a map object that I have to wrap in list to see what the results are. I don’t think I have ever wanted that. map should map the function over the collection and return the results as a list, like it used to. Stop making me think!

      Edited again: Also, doing anything async related in the REPL is just painful.

      Javascript: Ok, I don’t really love JS. But I do a lot of work in it. And I think adding generators was a mistake. They’re so rarely useful to anyone, and they’re difficult to reason about and test compared to… anything else.

    32. 3

      I think C# would’ve been better without properties.

    33. 3

      Python has batteries included but some of them are puffy and corrosive.

    34. 3

      Haskell: It’s incredibly burdensome to get a usable environment to work. Especially since everyone has started pushing the LSP stuff.

      (Common) Lisp: It’s standard library has too many compromises to please groups from the 1960’s. The file system tries to support everything from case insensitive file systems to ones without directory hierarchies. Most things practical issues require installing the same external libraries again and again.

    35. 3

      Rust allows you to build structure to your code, but this structure can easily turn into a maze where you need to “just know” about the trick door that lets you get to the end.

    36. 3

      Common Lisp has too much ancient baggage. In practice people just don’t use the clunky archaic parts, but it would be nice to not have them.

    37. 3

      Scheme: I’ve never actually programmed in Scheme or any Lisp dialect. I’ve just learned to read it well enough to follow along in SICP and SICM. I find it beautiful and ineresting, and Sussman uses it brilliantly to convey his ideas. Here’s the problem, nothing in the language specification requires it to be readable for humans. We depend on the author judiciously inserting white space, line breaks, spaces, tabs. This is a convention, not a requirement of the parser or compiler/interpreter. Even when well-formatted, I still find longer snippets difficult to read. I don’t care for the blizzard of parentheses.

      F#: This has been my go-to language for over 6 years. I have 2 problems with it: 1) Too much of the Microsoft F# team’s valuable time has been wasted in the past keeping it compliant with changes to C#. Ironically most recently this had to do with C# nullable types, which was an attempt to catch up C# with F#. 2) The temptation to do overly-sophisticated things with the F# type system is just too great. The type system is good, but not that good, and improving it and cleaning up existing issues, like the problems with statically resolved types (basically making SRTPs more useful), is often hindered by problem 1.

    38. 3

      Rust: long compile times. Far and away the biggest problem with Rust. Luckily, I’m not the only one who feels this way, and many people are working on it.

    39. 3

      Rust: For me, I get confused by the fragmentation of solutions that exist for common problems out there. Asynchronous programming has been one of the most anticipated features for Rust and I still don’t know what will be accepted by the community. We have tokio, async-std, and runtime just to list a few. Having choices is nothing to complain about but they all have their own interpretation of what asynchronous programming should look like. I know this feature is stabilizing in Rust soon so hopefully that will settle things a bit.

    40. 3

      Python: It’s slowly developing static types… without the benefits of becoming a compiled language or at least having significant speed improvements. asyncio is a mess and the packaging is a well known never-ending source of problems. Transitioning from Py2 to 3 with hard compatibility-breaking changes was a terrible idea.

      Nim: It’s unpopular. The async system and packaging are in line with Python.

      PROLOG: It lacks a modern-looking syntax to fit the popular taste.

    41. 3

      Rust: like how burntsushi said, compilation times yes it’s the worst and also the most boring complaint, but I’d say part of this is how easy it is for a dependency to pull in a massive number of proc macros, overzealous generics, and outdated versions of crates you use elsewhere. I guess that can be mitigated by simply living as a hermit and not using other peoples’ code though. My real gripe with the language itself would probably be how coarse lifetimes are. It’d be nice, for example, if I could more easily produce split non-overlapping borrows on collections without transmuting lifetimes. It’s a hard problem to solve though, about hard enough to where I don’t understand what it’d take to solve it

    42. 3

      Python: namedtuple is awkward.

      Rust: The compiler is slow.

      D: The community is so small. I’m skeptical about the meta-programming power because it makes the fast compiler arbitrarily slow. Some defaults are done the wrong way, e.g. default is unsafe and you have to annotate @safe.

    43. 3

      I guess I’m a little bit late, but I love Emacs.

      1. Concurrent operation support is still really lacking. It’d be nice if most calls got their own thread or something, so that downloading mail doesn’t hang my whole operating system (which is basically what Emacs is, right?)

      2. Maybe something of an extension to the above, but while Elisp is still waaay better than most other languages I can configure editors with (except maybe Fennel in TextAdept, but I can’t do all my Emacs stuff with TextAdept) it’s indisputably pretty bad these days. Replacing it all with a more modern scheme would be nice, but I think it’s definitely unrealistic. (What I absolutely do not think is the answer is to simply reimplement everything in Rust.)

      3. I run Emacs in a terminal, which is general considered not that worthwhile because GUI Emacs has so much good stuff. I don’t run a display server a lot of the time so it’s just the easier thing to do. But the way that the graphical Emacs runs is a bit of a mess, and doesn’t really work in the way that a GTK application is meant to. It’s definitely a hack.

      4. Like most big old pieces of software with a long history, Emacs comes with a bunch of legacy support for hardware and software which pretty much no one uses. I’d argue that if you’re still using a 40-year-old operating system today, you definitely have the nous to maintain any software you might need as a separate fork, leaving the main project with a cleaner codebase and less wasted resources making sure that stuff still works.

      5. Syntax highlighting support is not great. So syntax highlighting is provided by a mode, normally the major mode but sometimes minor modes like to get in the fun. Emacs doesn’t come with major modes for a lot of languages which are pretty common these days (Lua, Markdown, web-mode), which means that not only do I miss out on language-specific features, but also that I don’t see any syntax highlighting by default. Vim doesn’t have this problem, gosh darn! In the same vein, syntax highlighting is slow. Font-locking a hundred-line org buffer on my phone visibly drags it. Using some kind of PEG system for syntax highlighting would be cool.

      And finally, because 1 and 2 were kind of similar, and also because 6 is a perfect number after all:

      1. The packages Emacs comes with out of the box are an ecclectic mix. It’d be nice to have a sort through and prune out a lot of the junk (in my opinion). For example, viper mode is available by default, even though Evil is a significantly better vi emulation. Emacs has a rich history and in some ways pulling out the bad stuff is kind of chipping away at that (following this logic, tetris would almost definitely go), and I don’t know if I like that, so it’s difficult - but I still think it’s a bit of an issue.

      Regardless, Emacs is a pleasure to use and I like it to bits.

    44. 3

      English: I know you mean programming languages, but since I’m not a native English speaker I consider English a tool. Something I don’t like about English is how random written English seems, specially vocals, they have so many sound variations, that’s specially difficult when you learn vocabulary by reading, sometimes I know the written word, but if someone pronounces it I can miss it if that’s the first time I hear it.

      1. 2

        This happened to me today. I was saying “init” to a co-worker, and he didn’t understand at first because of my pronunciation.

    45. 3

      Python - Though I’ve gotten used to them, the syntax for list comprehensions is unnecessarily obtuse, especially considering how un-clever the rest of the language is. Also, some of the libraries adopted as standard like argparse, logging and test_unit are straight lifts from Java and are pretty awful to use. The community has built far superior offerings - pick one!

      Perl - This is 2019. I totally resent having to manually unwrap references in data structures and pop values off the argument stack. Having the flexibility to do these things in non standard ways just isn’t helpful. I’d rather focus my energy elsewhere, and when i’m forced to read code where people do crazy things it’s just painful and awful and WHY?

      Bash - the array syntax seems unnecessarily verbose and easy to screw up.

    46. 2

      I saw this thread coming from a mile away. ;) This is actually one of my favorite interview questions; IMO if you love a tool enough to actually use it regularly, you should have found pain points in it.

      • Rust: Ownership is hard to grok (which is not helped by borrow checker limitations, so things you can easily determine are safe in your head may require significant modification to pass the borrow check). Ecosystem is very unstable. Severe language-level feature creep.
      • Kotlin: Nullability is awkward and limited in more complex cases (e.g. there’s no way to have the equivalent of Option<Option<T>>). Ditto for smart casting (e.g. you can never smart cast a var field). It has sum types, technically, in the form of sealed classes, but they’re rarely used. On the JVM, inherits a bunch of JVM weaknesses (e.g. all those non-nullable fields are actually nullable, and are in fact null during initialization). Tooling is surprisingly rough, given that Jetbrains is a major sponsor.
      • SQL: Syntax is mega-awkward. Composibility is nonexistent. APIs are surprisingly bad. SQL injection. It scares people enough that we now have to contend with ORMs. (All this is, IMO, a small price to pay for a very widely-supported, well-understood mostly-declarative query language.)
      1. 3

        SQL: Syntax is mega-awkward. Composibility is nonexistent. APIs are surprisingly bad. SQL injection. It scares people enough that we now have to contend with ORMs. (All this is, IMO, a small price to pay for a very widely-supported, well-understood mostly-declarative query language.)

        I would love to see a modern take on SQL that emphasizes composability and reusability without resorting string concatentation/eval. I just find myself writing the same queries over and over again in different contexts and it would be nice if I had an environment that could be built up with tools to remove some of that repetition.

        E.g., finding duplicates in a table:

        SELECT Name, COUNT(*) FROM Person GROUP BY Name HAVING COUNT(*) > 1

        SELECT Email, PhoneNo, COUNT(*) FROM Contacts GROUP BY Email, PhoneNo HAVING COUNT(*) > 1

        It feels like 95% of the query is repetitive and I’m just filling in holes.

    47. 2

      Clojure: Dependency hell with Leiningen. Occasional very confusing performance issues with the JVM. Dealing with IO stuff is always very frustrating (although this is probably more on Java). Very easy to make mistakes with lazy sequences.

      (SWI-)Prolog: The module system seems very “leaky” and, while the “pack” system is nice, there isn’t a built-in way to version the dependencies.

    48. 2


      1. I need to be running a JVM, including in production.
      2. Java only dependencies can introduce dependency hell. Ask me about writing Excel format files. Or don’t, it’s been a while.
      3. Checking for empty or null with seq, as suggested by upstream, is not as easy to read as just (not (empty? Thing-Here))
      4. It is but straightforward or commonly documented for how to do repl driven development, specifically with choosing active namespaces automatically on repl startup. It’s easy to do, just one line, but not without hunting for that line.
      5. The abstraction leaks too much when switching from the JVM to JS. I don’t want certain library functions to need a different signature in cljs than in JS (looking at you, transit).
    49. 2

      Haskell It doesn’t have dependent types and it has a garbage collector, so I keep reaching for Rust or something like Idris, thinking it will better suit my problem, but I always end up back with Haskell.

      I think being a Haskell maximalist is the new bitcoin maximalism.

    50. 2

      I like Scala. I worked at two jobs and a startup where I did a lot of professional Scala work.

      I hate some of the overloaded operators used in some libraries, as well as how complex some libraries are. That combined with implicit to make Domain Specific Languages is really terrible. You can’t understand what’s really going on with some tools, or fully dissect error messages, without a PhD in category theory.

    51. 2

      F#: Should have been more like OCAML++/Haskell and less like “FP for nerds who don’t wanna C# no more” Type classes, functors, better notation. I could go on.

      C++: What the hell did you do to this? Is Keep It Simple, Stupid just too hard to understand?

      HTML/JS/Web Tech in General: See C++ above. Also, who is the genius that came up with the idea of trying to reproduce the entire universe of technology inside the browser, from data storage to low-level code?

    52. 2

      PowerShell has a horrifying number of “gotchas” you need to be aware of for complex scripts/modules.

      1. 3

        C++: What the hell did you do to this? Is Keep It Simple, Stupid just too hard to understand?

        Are you really sure you love C++?

        1. 1

          Ha! Fair enough, but the irony here is that we’re both correct: C++ started off as being a monster. That made good C++ coders into good managers of complexity. So why would a language that started off being a bear just continue to complexify itself.

          I got out just after STL took off, which was a good addition. But it just kept getting more and more good additions. It’s like the folks who designed the language responsible for teaching devs to keep it simple didn’t understand their own language paradigm.

    53. 2

      Scala. sbt is great once you learn it but learning it is like learning rocket surgery. Compile times will only get faster. Implicits are great but they can really suck to debug without a good IDE. IntelliJ has only gotten good with them in the last year or so. When I started doing Scala in 2013, my team simply avoided implicits because of how difficult they were to trace.

    54. 2

      Scheme: implementation fragmentation, especially when it comes to bindings with external libraries. R7RS small, and cond-expand have certainly helped, but I don’t think it’s quite there yet.

    55. 2

      Go: reading from nullable columns with the default sql library is, in my opinion, needlessly painful.

      1. 3

        I would agree, but since I’ve had no end of trouble related to nulls, I’m actually quite fine with just saying everything must be not null.

        I wonder what the best alternative would be for scanning a null value. Leave the input value alone and don’t change it? That would allow picking sentinels like -1 for ints, instead of 0, but I feel may as well put the sentinel in the database then to avoid code scheme mismatches.

        1. 2

          I 100% agree that nullable db columns are an antipattern, but unfortunately for me I didn’t have a choice in this case :) I would just like the sql library to provide a mechanism to default to the Go zero value for nullable types, at least in the case of strings. You end up doing things like

          var retrievedVal string
          val := sql.NullString{}
          // usually a BUNCH of vals
          if val.Valid {
              retrievedVal = val.String
          } // usually a BUNCH of these

          And it’s just a lot of junk to do for each field, and without any sort of generic code support, writing something general would involve some ugly reflection I suspect. But to be sure, not having nullable db columns would be my preferred way to handle this.

    56. 2

      TypeScript: if and switch are statements rather than expressions. In Rust you can assign to the result of an if but in TypeScript you have to use either a ternary or a mutable variable.

      1. 1

        Yeah, not having expression oriented constructs is very annoying.

    57. [Comment removed by author]

      1. 1

        Maybe you know, but Python modules can exist in different files if they are all in the same directory. It is annoyingly complicated to work out, though.

        1. [Comment removed by author]

    58. 2


      • Does a bit too much magic in its parser. Calling functions without parenthesis, combined the compiler trying to figure out what -> should be associated with in a case expression, can make your parser error messages or incorrect runtime behavior really painful to figure out.

      • Should have had mix release built in from the beginning. It’s a language specifically designed for writing network servers; there’s a reason Go made such a big deal of creating self-contained binaries.

      • Probably should’ve had pipelining put the piped parameter last, for consistency with Erlang’s existing libraries.

      • IntelliJ-Elixir is very, very slow. I’m not sure if the plugin is just badly-written, or if IDE work is excessively hard in Elixir.

      1. 2

        Calling functions without parenthesis

        While I agree, it would make language much harder to use, as function definitions would look like this:

        def(foo(a, b), do: a + b)

        As def is macro, and macros are still functions.

      2. 1

        Probably should’ve had pipelining put the piped parameter last, for consistency with Erlang’s existing libraries.

        How could this improve consistency with Erlang?

        1. 2

          A lot of Erlang built-in functions, like in the queue module, put the object being mutated last. This makes it painful to use the Elixir pipe operator with it.

    59. 2

      Ruby: the Array() constructor doesn’t really do what people want but people keep using it because they think they “should” do that when they want to wrap a pre-existing object instead of just using a literal. It’s a bit too easy to accidentally create a local variable when you were trying to call a peer instance method. Rubocop’s constantly expanding and ever more squirrelly and specific formatting rules have long since passed the point of diminishing returns, but the updates and new rules continue apace. Also the community has a significant, frustrating portion of developers and bloggers who think they’re writing in Java or some other static language, and really mis-apply patterns from those languages in Ruby without doing enough thinking about whether or not there’s a less tortured way of accomplishing what they want that would make more idiomatic use of Ruby’s strengths (blind enthusiam for an overuse of parameter-based dependence injection being a super obvious example of this).

      Scala: SBT fucking sucks, and the spotty documentation does absolutely nothing to improve the situation. Scala’s Either with Left and Right having “conventional meanings” is fuckawful and insane and coworkers seem to love using it to write impenetrable, opaque code – about half the time they get the convention backwards, probably because the convention is backwards. Implicits were a mistake. Also the community also seems to be drifting away, and there’s a lot of wheel-reinventing required if you want something native feeling instead of using Java libs and feeling like you’re writing Java in Scala.

      1. 2

        As a non-rubyist, I’m curious what the alternative to the overuse of paramater-based DI is?

        1. 4

          It basically boils down to, someone wants to do something like test some class that has some functionality that depends on (picking an example off the top of my head) Time.

          class Foo
             def some_function_that_depends_on_time
                Time.now + some_other_values

          And then someone comes along and says: “that’s not easily unit testable. The Right Way™ to do this is to pass in the dependency on Time!”. So they write something like:

          class Foo
              def initialize(time_impl)
                  @time_impl = time_impl
             def some_function_that_depends_on_time
                @time_impl.now + some_other_values
          # in your unit test:
          class FooTest
            def test
               foo = Foo.new(TimeMock)
               # do some testing with foo

          or they use a default argument on the method itself

          class Foo
             def some_function_that_depends_on_time(time_impl = Time)
                time_impl.now + some_other_values
          # in your unit test:
          class FooTest
            def test
               foo = Foo.new
              #  some test involving foo. some_function_that_depends_on_time(TimeMock)

          and then they all pat themselves on the back and congratulate each other for loosening encapsulation, increasing API surface area, leaking implementation details into the public API contract, and just all around making refactoring code and reasoning about things harder. But hey, it’s the only way to make it predictably testable, right?

          Well, no. You could just accept that you’re writing a dynamic language, Ruby, instead of a static one, and use that to keep the API nicely encapsulated while still keeping things perfectly testable:

          class Foo
             def some_function_that_depends_on_time
                time_impl.now + some_other_values
              def time_impl
          # in your unit test:
          class FooTest
            def test
              foo = Foo.new
              foo.instance_eval do
                  def time_impl
              #  some test involving foo. some_function_that_depends_on_time

          There are mocking libraries out there that give this a simple DSL. But people still avoid this route out of some kind of perception that they gain more moral purity by avoiding dynamically overriding methods, despite working in a language whose key feature is essentially just that. Writing Java in Ruby, and accepting the attending harm to APIs and encapsulation in a language that makes refactoring a lot harder than Java, strikes me as just sloppy and poorly thought out.

      2. 1

        Like @yumaikas I’m curious what you see as an alternative to parameter-based DI. I mildly prefer it because it makes collaborator objects more-obvious, and lets me treat object instances as immutable rather than exposing accessors.

        1. 1

          I already posted a long example in the reply to his comment? Keep your implementation details private and use the dynamicism of the language to inject collaborator overrides in tests via instance_eval and other limited forms of monkeypatching.

          Making collaborator objects “more obvious” is one thing if it doesn’t come with significant downsides, but making them part of your public API contract is a terrible abandonment of encapsulation, in my opinion, and makes all future refactoring and what-should-be-private implementation detail changes into public, breaking changes to the contract.

          It works in Java because you have type restrictions on method arguments. If you have some method public int foobar(timeCollaborator: SomeTimeInterface) then your contract with the outside world is “implements SomeTimeInterface”. The compiler enforces that contract on both the outside world, and on yourself, and changing internal implementation details around method calls on timeCollaborator is information that does not leak across the method boundary.

          Ruby doesn’t work like that. At all. If you have:

            def foobar(time_impl = Time)
              time_impl.now + whatever

          the only contract you have with the outside world is “time_impl responds to the method now”, and the only things that enforce that contract are your discipline (on your end), and runtime exceptions (on the outside world’s end). If you change that method call to Time.some_other_method, you have broken the contract and that is a breaking change to the outside world. Ruby is duck typed, and there was never any requirement that client code conformed to the interface of Time (an unrealistically high burden that no caller would bother with if they just wanted to do some manipulation of how your particular gem dealt with that collaborator), just that they implemented the specific methods you happened to call.

          Internal implementation details leak like a sieve in Ruby if you’re not careful; good API design requires ruthlessly minimizing API surface area.

          1. 1

            Ack! Sorry I had a tab open to this and didn’t see your response when I posted. ANYWAY.

            I think you explained well-enough :) I don’t agree that instance evaling to override a private method is superior just because you can - in fact Time is one of those things I have found to be useful to ruthlessly parameterize because it’s useful to use beyond testing - e.g. needing to back-date something. But also I don’t think it really solves the Time interface problem to substitute it as instance eval’ed, though at that point you are deep in the internals for testing anyway I guess? If you’re trying to protect the caller (and not testers) from being able to use alternate objects or types, that does accomplish that, but in any duck typed language you have that for any object you pass around. In practice it doesn’t break as much as static type folks think it would - it’s a bit of an impossible language to code defensively.

            Anyway, point well taken and apologies for prompting you to rehash!

      3. 1

        Implicits were a mistake

        Do you think typeclasses are a good or bad idea? What would you use instead/replace them with?

    60. 2


      • It’d be super nice if it had a properly supported GUI toolkit, even if it was just TCL/Tk.
      • I’m the group that can live without generics, but would really like to have them
      • Error handling is more verbose that I’d like it to be. It works ok in webapps where you can just panic if you’re needing to stop the request, and can set a global handler to do the right stuff, but it makes exploring ideas a lot more tedious


      • The deployment story for anything written in C# is messier than I’d like, between ClickOnce, publish, setting things up in IIS, and so on. It’s not terrible, but it’s not nearly so easy as Go
      • Equality is messy in C#. To do it right, you have to define an operator, implement an interface, and work out a way for your object to generate hash values.
      • Microsoft has at least 3 ways to accomplish many important things, with different tradeoffs:
        • User Interfaces (WinForms, WPF, Silverlight, etc),
        • background threading (BackgroundWorker, TPL DataFlow, Threads + BlockingCollection, etc),
        • Websites (ASP.NET MVC, WebForms, and the versions thereof)
        • Database access (Linq to SQL, Entity Framework, ADO.NET, which seems to sit under the other two)


      • The documentation is pretty sparse. It’s there, but one ends up rolling one’s own versions of things more
      • 1-based indexing is a big turn-off for a lot of people. I don’t mind it, but it is a transition cost when moving in/out of it.
      • function instead of func or fn makes me sad when I want to write higher order code.
    61. 2

      Clojure - so elegant and fun to write coming from an OOP background. I constantly get hung up when you need to bring in new dependencies due to classpath weirdness. Sometimes, restarting the repl is the fix, other times not.

      Python - Love the brevity of it, but I would love some editor help with parameter types. I have to try out mypy still. I use PyCharm, and I’m constantly surprised how little autocomplete and keyword highlighting helps me make sure I am using valid keywords, types, etc. Maybe I’m just underwhelmed with the tools I use….

    62. 2


      1. It does not plays well with other programming languages and cgo is a mess. This results in situations where you would feel everything is going well right until the moment you have to interface a program written in Go with a program written in any other language. I don’t like this and I believe it should be more co-operative.

      2. nil.

      3. Lack of enums. You can sort of hack your way around with a custom declared type but it’s not pretty and I’d like to see better builtin support for those.


      1. It’s a vast language and it was much more intimidating for me given my background was in writing Go almost exclusively.

      2. I had tried to get into async/await in the past and it’s all very complex and confusing. I gave up a few times after reading random blog posts and tokio docs and only recently(read, this week) got into async/await by reading up the async/await book and just experimenting with it.

      3. Lack of libraries. Go has a very good library ecosystem and I miss that in rust.

      4. I assumed traits were like interfaces and made huge refactors in my code and only later learned that they are actually not like interfaces and I had to undo all the work I did.(learnt some important lesson that day). The issue I faced was, There is no safe way to get the underlying concrete type from a trait.

      5. Compile times

    63. 2

      C: having tons of ways to interpret (). As a ( *function ), as container for parameters to a function(p1,p2), as a cast (int)long or as the simple mathematic precedence to calculate 2+(5*3) in the correct order. Somehow the compilers gets it right even though I might not always.

    64. 2

      Love Clojure, but like everything it’s not without its problems. I think that conj having a type specific behavior was a mistake, especially for a dynamic language. When you use conj with a vector it appends, but with seqs and lists it prepends. Seeing how most standard library HOFs will return a seq it’s easy to miss the type changing from under you.

    65. 2

      The Go runtime is terrible and rules out Go’s applicability to a huge set of problems.

      1. 2

        Do you mean the garbage collection itself, or how it implements it?

        1. 3

          I’m referring more to Goroutines, though garbage collection imposes similar problems. Because goroutines can switch more or less randomly (at least from the programmer’s point of view) between green threads and real threads, all programs have to deal with the problems of the latter. If it were up to me I’d never use real threads and my code would be 100x simpler for it.

          1. 8

            I don’t think about threads when I’m writing Go. What are the set of problems where green threads switching to system threads is undesirable?

            1. 1

              If a program is using just coroutines (green threads, but I’ll use the term coroutine for this) then only one coroutine is running at a time, so I can be pretty sure a sequence of instructions will be “atomic” with respect to other coroutines. With true, preemptive threading, all that goes out the window.

              1. 2

                I’m really confused by this exchange. One of the primary purposes of goroutines (coroutines, green threads) is to exploit the parallelism of the processor. This naturally requires synchronization for shared memory access. Are you and Drew saying you don’t care about it and don’t want to think about it?

                1. 1

                  If I want to exploit the parallelism of the processor, I’ll run multiple instances and have them communicate (the actor model). Shared memory, in my opinion, is evil and makes reasoning about code very hard to impossible, depending upon how extensively it’s used.

                  1. 2

                    Isn’t that the whole idea behind using goroutines communicating through channels rather than threads modifying global state with mutexes? You could in theory write Go code with a bunch of goroutines modifying global state, but I don’t see why you’d do that when you dislike it so much and channels are so frictionless.

                  2. 1

                    That’s certainly an approach, unfortunately made more difficult by all the work you have to do to get those instances to behave nicely if you want to serve (say) 10k QPS on a single port.

            2. 1

              I think one key factor helping go here is that it leans so much on copy semantics. When you can avoid reference types in concurrent code (I mean, you usually want to do this in other languages too) you’re almost exclusively dealing with stack allocations and threads are a non-issue.

              If you’re writing code that mutates something on the heap, you need to remember to put a lock around it in go, because you don’t know what else might be fiddling with it.

              In python/ruby you don’t have this problem since there’s a GIL, and in rust you don’t have this problem because it won’t compile.

      2. 2

        Can you list a few of those problems?

        1. 3

          The only possible response to running out of memory is a fatal panic.

          1. 2

            How much actual software does anything more constructive in that case? Heuristically, it’s so little that the Linux kernel (however controversially) doesn’t even give applications the chance—every allocation succeeds, with the OOM killer as a nuclear pressure relief valve.

            This is not to say that it’s not a significant failing of the Go runtime, but I doubt it’s one that “rules out Go’s applicability to a huge set of problems”.

            1. 4

              I have found it a considerable obstacle to writing reliable servers on openbsd, where memory limits actually mean something. I don’t like it when my entire web server goes down because one request happened to be a bit large. I would like that request to fail and for other to continue. Or at the very least for some finite number of requests to fail before order is restored. I can certainly write such code in C.

    66. 1

      I like JavaScript, especially TypeScript. But just about everything touching it is a garbage fire.

      • Community obsessed with polluting the global package namespace trying to make a name for themselves
      • Published artefacts full of garbage like test code
      • Global namespace. In fact, just about every decision made by the npm team
      • No (easy) ability to publish artefacts for specific environments under a single name (like es5/node, modules/node, browser, etc)
      • Everything is designed to be as small as possible, requiring thousands of packages and dependency bloat

      I could (and do) go on…

    67. 1

      Haskell’s indentation aware yet extremely fragile syntax, also laziness.

    68. [Comment removed by author]

    69. 1

      Typescript has been very pleasant to work with, but sitting on top of the JS ecosystem is a blessing and a curse.

      JS itself is becoming less horrible, but again the underlying ecosystem is a clusterfuck.

      Clojure is fun and interesting to play with, but I really, really like types.

      Haskell is also interesting, and has types, but the amount if effort to become proficient is plain intimidating. I wish I’d found it in my 20s (rather than my 30s) when I could have aunk some serious time into it.

    70. [Comment removed by author]

    71. 1

      Over the past 5 years, I’ve been thinking of what programming language I truly 💙 (or want to), and two contenders have emerged.

      Common Lisp: I write Clojure for $DAYJOB, but right at this moment, my biggest problems I have with CL are the modern niceties that Clojure has. Clojure’s lazy sequence abstraction and the rich standard library for manipulating them, first class concurrency primitives, immutability and persistent data structures. Many of these things do exist in various forms in the CL ecosystem as libraries, but it is by no means standard practice.

      Perl 6: From what I can understand (barring the complexity of the language), the only real hindrance to adoption is the performance issues, and fledgling library ecosystem. They seem to be actively working on performance, so it will be interesting to see how it turns out a few years later.

    72. 1

      For C and C++, speaking as a novice programmer who grew up with BASIC on a TRS-80 Color Computer, I have to say that I dislike that every time I declare a new variable I have to remember to set it to zero or an empty string depending on my needs or else there could be some sort of garbage in it. It would save me a lot of extraneous typing of things like “int foo == 0;” if the variable could start off blank or zeroed when first declared. I know this is a small thing to quibble about but for me personally, this sort of thing irks me.

    73. 1

      Ruby: if your only parallelism story is processes, then pretty please with sugar on top can we get that compacting garbage collector?

    74. 1

      Ruby: I didn’t like Rails when it was still new(ish), and I still don’t like it, all the way up to today. Sadly, Ruby was and is too often conflated with Rails into one single thing, which they aren’t. The ecosystem has hardened so much by now though, that you can hardly find developers or gems that don’t assume you’re using Rails. On a minor note, I am occasionally jealous of some of Javascript’s syntactic sugar like destructuring.

    75. 1

      Go: has constant strings, but not constant byte slices.

    76. 1

      Scheme: The already small ecosystem is deeply Balkanised and writing a large Scheme program that works on multiple implementations is nigh impossible without expending great effort on portability. You have to pick an implementation and stick to it. As a consequence, the library selection is scarce. Even Common Lisp is better than Scheme at cross-implementation compatibility.

      Clojure: I can’t stand the worse-is-better approach that deviates from the Lisp tenet of do-the-right-thing. The “let someone write a library for it” principle puts me off. The majority of useful libraries that do something that really should be in the core library are abandoned on GitHub.
      Edit: Oh and Clojure’s nil punning is a ridiculously bad design pattern and you can’t change my mind about this.

    77. [Comment removed by author]

    78. 1

      Go Why make dependency management hell?

      PHP Why didn’t we get typed class properties when we got typed function parameters?

      1. 2

        Have you tried the module system in Go >= 1.12 ?

        1. 1

          I have to admit I haven’t in any great detail; I am aware of it and from what I have seen it looks a little more complex than I would have expected. I have a few projects that I plan to begin using it with, its just finding the time to do so among all the other development work.

    79. 1

      Ocaml: surprisingly poor support for namespacing modules, usually bites me when I’m trying to link in a new library and it clashes with one of my modules.

      Racket: Memory usage of the GUI framework (100+ MB) makes me sad.

      Ruby: Seems to be standardising on a clunky syntax for type annotations. Python has a far more readable solution for the same thing.

    80. -5

      PHP: eugh.