Tell us why your language sucks

February 18th, 2008

Let’s play a game. Take your favourite languages and tell us what you hate about them. Like I’ve said before - all languages suck. Don’t pretend yours is perfect. If there aren’t things about it which annoy you you’re not trying hard enough.

Note: This should not be about bashing languages you don’t use or like, or even languages which you use and don’t like. Everyone does that already. I don’t want to hear yet another rant about why Ruby sucks from a Java programmer, why Java sucks from a Factor programmer, why Lisp sucks from a Javascript programmer, etc. I’m not even interested in hearing about people who are forced to use Java|C|C++|Javascript|Whatever and hate it. Tired, been done to death. It specifically has to be a language you use at least semi-regularly and like. Feel free to post here, to your own blogs, to the inevitable reddit thread, etc. But wherever you post it, have fun. Go on, rant. Get it off your chest. You’ll feel better for it. :-)

I’ve already started with Scala. Now I’ll do Haskell. Hopefully someone more experienced with the language will follow up, as I’ll freely admit it’s not my best language.

So, things I hate about Haskell:

Let’s start with the obvious. Monad tutorials. No, not monads. Specifically the tutorials. They’re endless, overblown and dear god are they tedious. Further, I’ve never seen any convincing evidence that they actually help. Read the class definition, write some code, get over the scary name.

Now we’ve over the one sociological issue, let’s get to some actual language ones.

Modularity. Haskell’s module system is about the minimum possible you can get away with and still claim to have a module system. Anything less and you basically have C style header files. Now, this isn’t a huge problem. To a certain degree type classes mitigate the need for anything functorial. But there are examples where it’s an issue. Take ByteStrings for example. Lazy and strict ByteStrings have essentially the same module signatures, but there’s no way to write code which is agnostic as to the type of ByteString it uses. They don’t live in a type class because it was considered that the type class signature would be too long. This is probably fair, but life would be a lot nicer if you could write functors over the ByteString modules.

In general, Haskell has very poor namespacing. If two modules define something of the same name you have to redefine one of them or refer to one of them qualified. It’s not a huge issue, especially as you *can* rename and Haskell modules are anyway not as long as Java packages. It’s not like you have to refer to org.haskell.data.collections.map.mypetcat.lookup. M.lookup isn’t too onerous. This mainly causes problems with symbolic operators, because things like Foo.++ look a lot worse as infix operators than Foo.bar does. I don’t know of a good solution to this problem. Object oriented languages provide a solution to it by basically making the type of the first argument responsible for namespacing. I don’t find this especially satisfactory, but it does work.

On a similar note, Haskell type classes basically live in global scope. There are good reasons for this, and it avoids you having to write a whole lot of painful sharing constraints (The example that is thrown at me every time I complain about this is what do you do if you want to unify two Data.Sets, each of which uses a different instance of the Ord type class)? It can be a real pain though.

There’s another issue related to type classes. Let’s pick an example. Data.List defines both sortBy and sort. The difference is purely that sort uses the standard ordering on the Ord type class while sortBy uses a provided ordering. And you need to define different functions every time you want to be able to use either a type class or something user configurable. Because you can’t redefine type classes locally or pass them as first class instances there’s basically no way around this. You can normally avoid the issue with selective use of newtype and careful choice of your types, but it’s definitely there.

Which brings me to another point - type classes are not first class. The Scala encoding of type classes is clunky in many ways, but a very nice feature is that the things you’re using as type classes are real first class members of the language, and you can bring to bear all the usual tools you have for manipulating stuff.

10,000 compiler extensions. GHC introduces so many compiler extensions, and everyone goes wild about them. This is understandable. Some of them are really useful. Some of them it’s amazing to manage living without (No multiparameter type classes in H98? Yeargh. Getitaway!!). I really like most of these I’ve used, but it sometimes make it feel like you need to employ deep and profound type system voodoo to get anything done. It would be very nice to get these unified into something sane and consistent, but Haskell’ seems to have stalled.

Tuples and records. There’s basically no good generic handling for either of these. Each of the different sizes of tuples is a totally different type (with no common type classes for abstracting over them), records are just a thin wrapper over normal datatypes with no extensibility or namespacing. Your record accessors will clash just as much as any other function name.

Something which has bitten me in the past is sharing code between monadic and non-monadic contexts. You essentially need to write pure and monadic versions of a number of functions. It would be nice if generic monadic code could automagically be specialised to the identity monad in a way that didn’t uglify pure code using it, or pure code lifted to monadic versions (this is harder I think). This doesn’t come up too much, but it means that there’s often functions like foo and fooM for pure and monadic versions.

The prelude and standard type classes are a bit painful sometimes. Things which you’d expect to be overloaded into type classes aren’t (Data.Monoid defines a function mappend for example, which in the List instance is ++. Why isn’t ++ on Data.Monoid?) and sometimes the type classes which are there are poorly thought out (Num shouldn’t extend Eq, and it would be nice if + was factored out in order to provide better support for things like vector spaces). I believe there is some work on alternate numeric preludes.

Plus lots of other little things which don’t spring to mind at the moment.

Also, one final disclaimer. Please please please don’t take this as a “Haskell sucks, don’t use it!!!” post. If you do I’ll… I don’t know, give you a really devastating hurt puppy look or something.

I’d also appreciate it if you don’t use this post to start a language war. Remember - you’re only allowed to say bad things about languages you actually like. Otherwise you’re cheating. :-)

29 Responses to “Tell us why your language sucks”

  1. FeepingCreature Says:

    D.

    Crappy error messages, especially for errors in string mixins buried in nested templates.

    The lack of traced exceptions in the standard library.

    Bugs in the standard library. Seriously, it’s 2008. I shouldn’t have to worry about this sh*t.

    The Phobos/Tango schism. Still not resolved. Basically, D currently has two “standard” libraries. There was talk of getting them on a common basis, but little in the way of actual results.

    Longstanding compiler bugs. I won’t even get started on this one.

    The glacial speed of the GDC project, which provides the only free D compiler in existence.

    Eschewing language polish in 2.0 in favor of flashy features that nobody really needs (like const).

    Underpowered, half-baked tuples. They’re cool, don’t get me wrong, but they could be much more.

    A lack of mature, cross-platform, native-D GUI libraries.

    .. that’s all I can think of right now. I thought there was more ^^

    –feep

  2. José Dinuncio Says:

    Python:

    It started as a nice, clean and elegant language. But it had its shortcomings. Its basic types weren’t subclasseable, no unicode, ugly __double-underscore-methods__, a tricky mechanism for setters and getters.

    Of course they worked out a lot of things. They introduced an object class, static and class methods, metaclasses, iterators and generators…

    But those are patches. The simple and elegant language design is not more.

    Hopefully, python 3000 will bring back the lost coherence.

  3. Ralf Says:

    Python:

    no switch, case statement
    explicit self in method declarations(don’t really hate it, but annoying)

  4. spamigator Says:

    C/C++.

    Lack of first-class functions (even if templates give much of the same functionality). No garbage collection. Replication of definitions in .h files. Typing system imperfect (doesn’t require casts for enums, doesn’t allow dynamic programming). Hard or impossible to mix code and data. Doesn’t scale well to increasingly common parallel systems (but virtually nothing does)

  5. John Cromartie Says:

    Ruby:

    The speed. I can’t wait for Ruby 1.9 to be production-ready. Ruby’s speed is rarely a limiting factor, and using an external tool to do something intense is not hard. However, it would be nice to be able to do some more intense stuff in pure Ruby.

    Rails. We all know it: Ruby is a language, Rails is a framework. But, for some reason, the majority of vocational programmers out there simply cannot separate the two in their heads. Whenever I talk about some feature of Ruby or some bit of code I wrote, coworkers scratch their heads and say something like “yeah, but why would you need that in a website?”

  6. CruxOp Says:

    PHP.

    I love PHP though. I chose to use it over Ruby for greenfield development. Yet the way new releases work annoys me.

    Backwards compatibility.

    The deliberate changes are the worst. It seems that new minor revision of PHP breaks existing code. There’s a change in the return of builtin function, or a change in the flags that it can accept.

    Why break BC? Why?! I’d understand if it the break came with significant advantages, but it doesn’t… it simply breaks documented and expected behavior for aparantly no reason except making the language pure.

    Bugs. All new PHP releases have a bug that breaks existing code. All of them. It gets fixed quickly, but still its no incentive to start running the latest release on a production site.

    And lastly, I hate the way PHP internals works. There seems to be so little unifying direction, and patches or improvements are usually ignored or shot down as not being the ‘php way’. What is the PHP way?
    I don’t know.

  7. spamigator Says:

    Scheme.

    Trivial to implement, so everyone brews their own. Dozens of systems, but no single usable system. You can find a Scheme implementing whatever single bit of functionality you want, but nothing with all the functionality you need.

    Syntax a little awkward — blocks and defining variables a bit obtrusive (like pre-C++ C, variable definitions create scope) and verbose. Indentation unclear. Uniformity makes it slower to scan code.

    Code inspection/modification better than in other languages, but could be much better, given the syntax (I should be able to manipulate code as a list).

  8. gnufied Says:

    Ruby.

    Somewhat buggy implementation. Crappy garbage collection. At times somewhat weak package/module separation.

    Buggy thread implementation (at least in 1.8.x). No warnings when you override a method defined in parent class or you open a class and override an already defined method. For second case, you can get warnings by using -w flag, but almost nobody uses it.

    I have been always shown the door, whenever I have raised last two issues.

  9. Eric Stein Says:

    Python.

    As mentioned above, lack of a switch/case statement. What I like best about python (its ability to be both functional and imperative) is left a little inferior to Haskell because of its lack of the beauty that is the haskell type system.

    I just wish Python was selectively dynamic instead of being completely… jiggly.

  10. Stu Says:

    Python:
    Speed ! It’s just not quick enough to do hard work… hopefully PyPy will fix this.

    + Mobility: I’d really like a standard jython distro for j2me

  11. Delackner Says:

    Objective-C (intwined with Cocoa)

    The flexibility is wonderful, thank you, Scheme. But I often sigh and feel like Icarus with some wax wings looking over at the real birds.

    When I am debugging, I can’t stand when the inspector shows an NSString and in the value field just shows some meaningless rubbish, when it is mind-numbingly obvious it should show the VALUE of the string.

    Why, in an environment that is natively UNICODE, does gdb still barf on Unicode strings unless they are in english? Unicode is not just double-byte english!

    > po [compiledObject knownMethod]
    error: I am too stupid to look up what the declared result type is, please hold my hand
    *ARGH!!!!*
    > po (id)[…]
    “hello”

    Give us simple syntax to use functions as objects.

    Screw C compatibility, wherever it impedes writing new syntax. As long as I can load C libraries, stop worrying about breaking C when the alternative is far better syntax.

    I want a compiler switch to force (except where explicitly disabled for performance-sensitive blocks) int, float, and double to be transparently converted back and forth with NSNumber, and NSNumber to just disappear in the background.

  12. Greg Davies Says:

    F#

    Can I please have some curly braces? What is so inherently incompatible about functional languages and curly braces around the functions?

    XML based programming languages like XSLT

    I love how easy it makes everything I do with XML, but the fact that a programmer has to write XML by hand brings shame to our profession.

  13. Henrik Says:

    Scala.

    Type erasure makes me sad.
    Number system like in Java, not possible to write common operations on all numbers without boilerplate. (Haskell’s way of doing this is so nice).
    Error messages are sometimes confusing.
    Type inference lacks the power of Haskell (for a good reason, but still).
    Verbose built-in syntax, like ‘match’ and ‘case’, but not a bid deal.
    Library API is documented poorly. (There’s going to be a community support for this, however).
    No metaprogramming system :(. This means Function traits and Tuples etc are script generated. Also hard to make generic functions that operate over different types of Functions or tuples.
    toString method on all classes with concanetation ‘+’ means there can be nasty bugs. anyType + “string”
    No properly working IDE yet (hopefully coming soon).
    Compiler bugs when dealing with advanced stuff or corner cases, though developers quickly fix important ones.
    No multimethods.
    No standard graphics library.
    No annotation for pure functions, unlike Haskell.
    Tail call optimization on JVM sucks.

    [optimistic]
    However, I’m convinced that many of these issues are in better shape at the end of this year.[/optimistic]

  14. T Says:

    Python:

    Not quite dynamic typing. Far too often I end up fixing a bug by wrapping an “int()” or “str()” around a variable. This may be more telling of my programming skills than the language itself, but when throughout the course of my program, ‘foo’ is always a number it drives me nuts when I have to cast it as such just so another method doesn’t take it as something else (what that something else is, I don’t know!)

  15. Danny Says:

    Smalltalk.

    Doesn’t play well with other tools: all those text manipulation tools in Unix; your favourite editor; your favourite source control system — all out of reach.

    Speed: modern JVMs kick the backside of any Smalltalk VM up and down the street in terms of performance, especially with the dogged persistence to use only green threads.

    And the UIs look seriously dated and crappy.

  16. tutufan Says:

    Python:

    - No good way to check whether “a.b” is valid or not at “compile” time, so you may discover at runtime, even in a program that’s been working fine, unchanged for years, that you forgot to import some library.

    - Programs like pylint, pychecker, etc., that might address the above problem, are usually targetted to a python one version older than the one you’re using, so they don’t help.

    - Similarly, distribution versions of python-mode.el are often behind.

    - I’ve had an open bug with a patch that hasn’t made it into the the trunk in five (?) years or so. No one seems to object to it–it’s just stalled.

    - When Python throws a type error, it’ll tell you that your type is wrong, but it doesn’t print the errant value or even its type, which leaves you to guess, or add print statements.

    All that said, it’s still my current favorite.

  17. Pseudonym Says:

    Wow, you’ve pre-empted pretty much every quibble I have with Haskell.

    There’s one I’ll expand on, though: Some data structures have logically similar interfaces (e.g. mutable arrays and Data.Maps), but one type “lives” in a monad (arrays) and one doesn’t (maps). It’s pretty much impossible to share a typeclass between them, or to easily replace the use of one with the use of the other.

    Now: C++

    Well-written template libraries are a dream to use, but they’re painful to write. Like, really, really painful.

    OK, let me rephrase that. They’re easy to use so long as you don’t write any type errors. Then, you have 1kb long error messages to deal with.

    Lots of things in C++ have four varieties: thing, const thing, volatile thing and const volatile thing. There is no nice way to introduce polymorphism here.

    Meta-object protocols are important in a lot of Serious C++ Applications, but nobody knows how to do them in a consistent and sane way.

    Compile-time-only template instantiation is a necessary evil. But it’s still evil.

  18. David R. MacIver Says:

    Yeah. The difficulty of sharing a type class between certain things is a real pain. Another good example of that (which I meant to mention but forgot) is that Data.Set can’t be a monad because of the Ord constraing on certain functions.

  19. Taylor Venable Says:

    Erlang:

    Why is it that runtime error messages produced to the user through the runtime environment have to maintain their internal tuple form? I shouldn’t have to decipher a very long {badmatch, …} tuple (especially when processing XML) to find out I’d written #xmlElement when I meant #xmlText.

    Also, syntax errors are terrible, and usually all you get is something like “syntax error before ‘.’” which makes it difficult for new users to adopt. I guess PHP is the same way (at least PHP4 was) in that it gives a compiler message like “expected TEND”.

  20. Friedrich Says:

    C:
    No GC, no bound checking, functional abstraction is “near non existent”.

    Eiffel:
    Sma(ll,rt)Eiffel, who needs backwards compatability?

    Common Lisp:
    easy of library use, stability of APIs

    Emacs Lisp
    scoping rules, lack of a decent Data Structures, non straigt-forward programming model

    Smalltalk:
    lack of decent Editors and Apps living up to the “quality” of Smalltalk

    Ruby: smalltalk like IDEs ;-), embeddebility

    Scheme: which scheme should it be today ;-)

    Shell: who can learn all the dependencies?

    Io: naming of List

    Self: not available that easy on the diverse Systems

    Haskell: sequential programming, error messages, debugging and tracing, a decent IDE

    Ocaml: Syntax, when to use ([ ]};
    how many ;?

    Visual Basic for Application: how can keep one track of versions and how can one build proper Datastructures with ?

  21. dkfn Says:

    Java

    Generics don’t make sense.
    Pervasive use of nulls and the resulting NPEs.
    No function pointers.
    No closures.
    No predefined map or list syntax e.g. {”x” => “y”, “z” => “x”} for a map.
    The static keyword.
    No mixins.
    No open classes / implicits.
    Many competing component systems (OSGI and JSR 277 and then one for every JEE spec, rar, war, ear, etc.).
    No language support for properties.
    Memory usage is pretty bad.
    Startup time is slow.
    No type inference.

  22. Alberto Says:

    Haskell:
    By far the best language (with Clean), and one of the greatests human achievements but:

    Haskell programmers are in an ethernal dialog with the (very inteligent) compiler, but when such intelligent being talks, sometimes the messages are obscure in his metalanguage of metatypes and meta-abstractions.

    Some runtime errors untraceable

    do not incorporate logic inferences, like Curry. So it is the half of a declarative language

    HS Plugins unsupported in windows!!!

    Monads: use them, but don´t create them, or else you will end up doing nothing practical, except playing with more and more abstractions until you abandon the sensible world.

    Curry:
    lack of Haskell extensions

  23. Victor Says:

    Haskell

    Absolutely the best language I’ve seen in my life.

    Most annoying

    MPTC: Infernal compiler errors and type signatures. If not this i’ll be mostly happy with them…

    Qualification of infix operators. At first it was truly shocking…

    Different string/list-types: normal [a]/String vs ByteString.Lazy, ByteString.Strict and so on…

    Monadic with pure. I really want something like liftMAll wich will convert pure function to monadic variant.

  24. Sohail Somani Says:

    Common Lisp:

    * Userbase
    * seat-of-your-pants libraries.

    Wish we could get the userbase of Python.

  25. L505 Says:

    Gonads?

  26. Daniel Fischer Says:

    Haskell: The best by far, but - see above - qualified infix operators aren’t pretty, the Numerical typeclasses are poorly designed.

    C: Arrays and pointers, I really hate how they did that. A pointer to thing is a different type than thing, so
    thing* p1, p2, p3;
    _should_ declare three pointers to thing, not three variables of type thing, one of which even has an illegal name. Similarly, the type is ‘array of int’, so int[] a; not int a[].

  27. daryoush mehrtash Says:

    can you please elaborate on this:

    Which brings me to another point - type classes are not first class. The Scala encoding of type classes is clunky in many ways, but a very nice feature is that the things you’re using as type classes are real first class members of the language, and you can bring to bear all the usual tools you have for manipulating stuff.

    Isn’t the “data” declaration the same as types in scala?

  28. david Says:

    No, Scala type declarations are kinda different to what you can define with data declarations. But that’s sortof irrelevant.

    The problem here isn’t types, but type *classes*. Monads are types, but Monad is not. ditto Num, etc. You can’t manipulate instance declarations as a first class member of the language.

  29. Mark Wotton Says:

    Haskell:

    ditto on moving stuff between pure and monadic formulations, and it just seems wrong to write everything in the identity monad just in case.

    Ben Lippmeier’s Disciple is an interesting dialect of Haskell that addresses this - it has an effect system that allows you to use mutating references safely without using a monadic syntax. Also fixes up the pretty awful record syntax Haskell has.

Leave a Reply