What simple things annoy you about your favourite programming languages?

mortoray profile image edA-qa mort-ora-y Jun 28, 2017

Every language has nuances. Sometimes they can be ignored, other times they get in the way, yet other times they lead to defects. Most disturbing are simple things that are missing, something that exists in another language, or something that seems like it'd be easy to implement. Perhaps it's just a minor syntax flaw, ambiguity, or library limitation.

What are the simple things in your favourite languages that give you grief on a regular basis?

markdown cheatsheet

[Delphi]
AnsiContainsStr parameters order: Haystack, Needle (documentation)
AnsiPos parameters order: Needle, Haystack (documentation)

This one has its share on me getting fired from a summer job, as I started shouting out loud in the office when I discovered it.

Vulgar APIs require vulgar outbursts. :)

Java Collections. Good stuff, but the API just didn't advance with time:

Map.get(Object)

You never know for sure if your key is a valid parameter, even if your generics tell, which key type you want to use. Always Object.

Want to create an ArrayList with one entry? Think this works?

new ArrayList<String>("foobar");

Nope, the constructor does not take items as parameter. Instead, use Collections.singletonList, but that one is immutable. Be surprised by your next RuntimeException. You could also use Arrays.asList, but not for a single item because that's considered inefficient.

YES. This is very annoying.

They're not part of the core language and you probably already know about them, but for those who don't:

Google Guava has Lists: Lists.newArrayList(E... elements)

Implementation: github.com/google/guava/blob/maste...

And (of course) Kotlin has the lovely arrayListOf(...)

kotlinlang.org/api/latest/jvm/stdl...

Python is my all-time favorite language, but I absolutely HATE packaging, installing packages, and working with virtual environments in it.

I've been told pip is getting better, and perhaps it is a bit, but it's still quite hard to get quite the right combination of packages working in the right scopes.

Thus why I've been saying for years: "Packaging in Python is like beating yourself to death with a wet trout." (So far, no Pythonista I've encountered has disagreed, even recently.)

Definitely. I felt that I was really just randomly copy and pasting bits into a setup.py file to get my install working. I never have figured out what the correct way to generate documentation is.

Probably appropriate:

Essential Python Packaging O'Rly

I really love Scala, but

  1. The compiling time can be so long (example: the classical "Hello World!", Scala: 4420ms (Without fsc) Java: 107ms (as a comparison)
  2. Values can be returned without return statement
  3. parameters always become immutable

(also if you're programming half of the project in Java, "++" can confuse you, because it does different things in Java and Scala)

What don't you like about #2, that values can be returned without a return statement? Are they named returns in the function signature?

You somtime accidentally return values (method type = unit (so you don't have to return null and can't get a nullPointException)) and sometimes it can take a while to understand that something gets return (like when a function does return true if it did what it should and that gets returned by another function)

Golang

  • Package management
  • I miss functional programming paradigm

I haven't used it yet, but doesn't Go allow any kind of functional programming? At least the basics of high-order functions?

Higher order functions, yes.
Anonymous functions, yes.
Recursive functions, yes.

But, no map, reduce, filter, pattern matching, and some other stuff that make the language as expressive and concise as FP can be.

Give a man map, reduce and filter and you feed him for a current task; teach a man to map-reduce with a recursive function, you feed him for his life.

Oh yes, this. Using Go sometimes feels like playing with barbed wire, instead of a sponge.

Golang is one of my favorites, but writing the same thing over and over annoys me more than anything. I'm talking, of course, about:

result, err := somethingThatCanFail(args)
if err != nil {
    return nil, err
}

Edit: I'm also aware that you can use named return types to do the simplified version below.

if err != nil {
    return
}

There should be a syntax shorthand for commonly used stuff like this. Rust has the try!() macro that does this, or even shorter, the ? operator.

This is one of my core complaints about Go. I'm quite opposed to explicit error handling since ultimately coders will just forget it in places, or not check it correctly. I'm in favour of errors propagating by default, and having a nice option to catch them if you want.

Any repetition is syntax is bad for readability. The intent of the code gets lost in overhead.

Using both Elm and F#. Here are a few things that irk me.

  • Hazardous generics
    Elm : Result SomeErrorType SomeType
    F# : Result<SomeType, SomeErrorType> - The sharp angles cut me

  • Elm union types do not allow an optional leading |, which makes the first one a pain to copy/paste, diff, etc. (Maybe implemented soon.)

    -- Elm
    type SomeUnion
        = FooCase -- merge conflict waiting to happen
        | BarCase
    
    // F#
    type SomeUnion =
        | FooCase
        | BarCase
    
  • F# let syntax is nicer to use. Elm let statements require an in clause whereas it is optional/implicit in F#. Additionally, elm-format always expands let statements to multi-line. The visually-enormous resulting code leads you to want to avoid let in Elm.

    -- Elm
    let
        asdf =
            oneLiner x
    
        fdsa =
            anotherOneLiner y
    
    in
        f asdf fdsa
    
    // F# - easier to read IMO
    let asdf = oneLiner x
    let fdsa = anotherOneLiner y
    f asdf fdsa
    
  • Elm generates curried constructors for all record and union types. Curried constructors are great because they support partial application. F# does not, and the normal record construction syntax is quite verbose. I often create my own curried constructor for partial application.

    -- Elm
    type alias SomeType =
        { someInt : Int
        , someString : String
        , someFloat : Float
        }
    
    -- automatic curried constructor
    let x = SomeType 1 "foo" 3.14 in ...
    
    let list = List.map ( SomeType 1 "foo" ) [ 3.14, 2.718 ] in ...
    
    // F#
    type SomeType =
        { SomeInt : int
          SomeString : string
          SomeFloat : double
        }
    
    // make my own curried constructor
    let mkSomeType a b c =
        { SomeInt = a; SomeString = b; SomeFloat = c }
    
    let x = mkSomeType 1 "foo" 3.14
    
    let list = List.map ( mkSomeType 1 "foo" ) [ 3.14; 2.718 ]
    

Here is what the last two lines would look like in C#.

    var x = new SomeType(1, "foo", 3.14);

    var arr =
        new double[] { 3.14, 2.718 }
            .Select(x => new SomeType(1, "foo", x))
            .ToArray();

Lack of optional types. Impossible to maintain large codebases in dynamic languages.

For what language?

Any language. Pure dynamic typing doesn't scale.

I agree with both statements. Dynamic languages are really great ways to quickly get something working and be productive. However, the moment that start exceeding a few thousands lines they become increasingly difficult to refactor.

In Leaf I've made optional one of the fundamental type qualifiers.

In Swift, it's often annoying that you cannot have a variable of a generic protocol type. You need to have a generic type implementing that protocol because you cannot specify associated types when declaring a variable.

For example, Swift has a Collection protocol, which has an associated generic type Element.

Instead of declaring a variable with let foo: Collection<Int> = ... you need to have a generic variable C: CollectionType where C.Element == Int, which can be pretty annoying because your enclosing structures also need to be generic.

Example

struct Foo<C: CollectionType> where C.Element == Int {
    var bar: C
}

It would be so much more pleasant to write

struct Foo {
    var bar: Collection<Int>
}

Yes I am aware of the AnyCollection<Element> type but that is just a workaround that is specific to the collection protocol and not a general solution and yes I am aware that the Swift compiler always wants to specialize generics at compile time but this is just annoying.

I despise the fact that null is an object in JavaScript.

[D] I cannot decorate enum members with user defined attributes :/

@("This works") int x;

@("This also works") enum E {
  @("This does not work") a
}

Inconsistency is a plague across so many languages. Features that can only be used on certain constructs. It creates all sorts of mental overload, and results in one not using all the features where possible.

The languages with garbage-collection are awesome! However, I do like the geekiness of managing memory for particular use-cases.

In Python:

"Hello" "World" == "HelloWorld"

This leads to bugs when you're writing an array of strings. This happens to me especially often when writing Django settings.py files

INSTALLED_APPS = [
  "foo",
  "bar"
  "baz"
]

This sets ÌNSTALLED_APPSto["foo", "barbaz"]`. I wish this would throw a SyntaxError.

C and C++ both do such string concatenation as well, though I've always considered it a feature. Put in this form it definitely will lead to unintended defects.

I wonder why it's there, given PYthon has string concatenation with a simple +.

Because

"hello" +
"world"

is a syntax error, since the + doesn't make python join the lines. You'd have to do

("hello" +
"world)

The fact that, in Python, "joining an array using a given string" is phrased as "use this string to join an array".

# this does not work, sadly
['a','b','c'].join('-')

# this works
'-'.join(['a','b','c'])

Now I'm used to it, but I used to get it wrong all the time, because 'join' is an active verb, so at least for me it makes sense to put it after the thing being joined.

Yup, I get that wrong every time I attempt to join an array. Logically join should either be a part of the array type or a standalone function.

PHP: was designed with the same level of consistency as The Lord of the Rings was
Translated into French: by at least four different people with different goals and who never talked to each other.

Ruby: gloats about Duck Typing as a revolutionary way of solving the problems of untyped languages, but you spend the first 2/3rds of every function validating your arguments just like in PHP or JS, only with different syntax.

Python: the amount of boilerplate needs once
your simple script starts to become long enough that it has to be split into modules.

JavaScript: The lack of any hint of a standard library (to format dates, for example), and the misplaced efforts to add more and more silly syntax before solving this problem.

To be continued…

Identify yourself 🤓