Ask HN: What is your least favorite part of your favorite language?

15 points by joelS ↗ HN
Complaints about a language usually come from people who dislike the whole thing. Those who are passionate about a language may be more insightful to its own shortcomings.

24 comments

[ 3.4 ms ] story [ 59.2 ms ] thread
Python: one case where I believe explicit is not better than implicit is the explicit passing around of self. It just seems to add extra clutter to the code, as well as demanding a certain cognitive overhead in remembering that a function is defined with N parameters but is invoked with N-1.

Perhaps there are good reasons for it, but generally I prefer java's implicit 'this.'

I'm not sure that it's a good reason, but the explicit self allows for weird intermingling of functions and members. For instance,

    class foo:
        def __init__(self,val):
            self.x=val
        def square(self):
            return self.x**2

    #imperative way
    imperative = [foo(x).square() for x in range(5)]

    #functional way
    s = foo.square
    composed = lambda x: s(foo(x))
    functional = map(composed,range(5))

    print imperative==functional

The explicit self allows you to treat member functions as generic function and compose them in a functional way. Now, most functional programming isn't necessarily Pythonic. However, given a proper compose function, I could imagine it being useful to mix a long chain of function and member calls without needing to make a distinction between the two.
Javascript - null vs. undefined. What's better than one keyword for a nebulous concept? How about two!

SQL - you SELECT fields before you specify what table they come from, so tooling is virtually impossible.

Ruby: Why do we need procs, lambdas, and blocks?

Haml: Hard to control whitespace around elements.

SQL: Can never remember which date functions go with which RDBMS.

C# : It only runs on MS stack (dont give me Mono)
Ruby: too slow. Love the language, makes me happy to code in, but compared with other languages I use (Common Lisp, Java, Clojure) it is slow. That said, for many applications execution speed does not matter.
The parentheses.
I found it a little confusing ready ruby code that omits parentheses, but sometimes I feel like a method call with no input params shouldn't need parens
None of the code I want to install on my hard drive is written in Scheme (except a Scheme compiler).

Also, Scheme cannot practically be used as an interactive command processor and file-name-space browser like a Unix shell can. (Yes, I know about scsh; I do not like it.)

Common Lisp: it's a 2-lisp

Javascript: semicolon insertion

Erlang: periods, commas and semicolons

Clojure: not much, actually

PHP: ugh, nearly everything

Python: lambdas can only have a single expression

Everything except Lisps: expression/statement dichotomy

Python: The fact that the datetime object set to midnight evaluates to False.
Lua indexes starting from 1 is a constant annoyance. Yes I'm used to it, and yes I remember, but it does always feel wrong.
Julia: 1-based indexing is annoying at times; a compiler would reduce waits on startup; error messages with multiple dispatch can be confusing.
Python: The import system. Why can't I easily import a file from any location in the file system?

Javascript: Lacks support for 64-bit integers and arbitrary-precision decimals.

SQL: SQL injection.

I agree with the python one. Some more things I find annoying in Python:

- Hardcoded recursion limit + no tail call optimization. I understand Guido's reasons for this but I find myself writing ugly code instead of beatiful code. (But maybe changing this would make Python's internals ugly.)

Mainly Python but other languages too. - Length of the collection: len(foo). String representation: str(foo). Why not foo.len() or foo.str(). Or even better: foo.len or foo.str. As a Python user, I'm not a fan of getters. I think we should use the "attribute syntax" to get data about and of the object/type/thing, regardless if it involves any computations or not. (but would probably generate more problems than solve little nitpicks)

I second the Python one. Why the wierd distinction between packages, modules, and things in files?

I believe all of these three should give me os.path.isdir:

    import os
    import os.path
    import os.path.isdir

    os.path.isdir(...)
And to get isdir() directly, I would use either:

    from os.path import isdir
    import os.path.isdir as isdir
This should work if path is either a package (a directory), a module (a file), or an object defined in os.py!

Sometimes, for some reason if I only import a module, I can't use submodules. So `import os` wouldn't give `os.path.isdir`. I don't know when that occurs, maybe it was in an ancient version of python. But it leaves me with an uneasy feeling every time I use a submodule. Anyone know when this happens?

Last but not least, it should be easier to import stuff from above the script. If my script lives at ./tools/foobar.py, and I want to import ./helper.py, it should just be something like

    # ./tools/foobar.py
    from .. import helper
    
    $ python ./tools/foobar.py     # simple run-a-script syntax
and not

    $ python -m tools/foobar       # hideous run-a-module syntax
or what I'm using now

    # ./tools/foobar.py
    # hideous boilerplate header
    import sys, os
    SCRIPTDIR = os.path.relpath(os.path.dirname(__file__) or ".")
    PARENTDIR = os.path.relpath(os.path.join(SCRIPTDIR, ".."))
    sys.path.insert(0, PARENTDIR)

    import helper

    $ python ./tools/foobar.py     # simple run-a-script syntax
Python: The fact that they broke the syntax with Python 3, splitting the language in two parts: one that is widely deployed, and one that is actively developed. I never had the luck to use Python 3 in actual work.

If I could, I would make Python 5 (2+3=5), which is Python 3 but accepts all the legacy syntax of Python 2 (especially the print statement).

I'd solve the str/unicode problem by providing

    bytes, str, unicode = python5.legacy_strings
or

    bytes, str, unicode = python5.modern_strings
that you would put at the top of the file. I'd make the modern strings builtin for system modules, or files with the .py5 extention. Problem solved.
Lisp:

(mapcar 'funcall (loop for x below 3 collecting (lambda () (print x))))

producing:

3 3 3 (3 3 3 )

OK, it is the loop DSL that chooses to rebind the same variable over which I closed, but loop is in the standard so I'll blame lisp. The syntax really looks like a fresh binding. Shucks, this is almost a bug: the loop implementation was allowed to leak into the loop semantics.

Go: no generics, the community is small, Go allows you to use functions as data (to some extent), but doesn't take advantage of this to the extent that it could. I'm relatively new to the language, so my observations may either be incorrect or not include the full picture. If anyone has anything to chime in or, I'd love to hear.
Objective-C: appending strings should be like every other language:

NSString *result