Ask HN: What programming feature can't you live without?

12 points by shakna ↗ HN
Monads? Type Safety? Hygienic Macros?

What's important to you, and why, when it comes to language features?

18 comments

[ 2.9 ms ] story [ 52.4 ms ] thread
Closures and functions being first class objects.

Thank you Swift.

It's a small thing, but function/method overloading and default arguments. Any modern language (hi Rust) without such basic programmer conveniences irritates me.
Curious to see your opinion on this:

Java's overloading syntax always annoyed me as being overly verbose.

But what if you accidentally overload?

How do you think it should look?

In my experience, most cases of overloading are trying to achieve one of two things:

* An argument with a default value: foo(string bar), foo(string bar, string baz)

* An argument which can be of two or more types: foo(string bar), foo(byte[] bar)

I believe the first problem is better solved by an optional/default argument language feature (or in a pinch, a sentinel value).

I believe the second problem is better solved by either using a dynamic language, or an expressive type system that lets you say what you mean, either via polymorphism or union types.

For the remaining cases it's probably acceptable to have a second function with a slightly different name.

While we're on the subject: Keyword-specified arguments are a major quality-of-life improvement, especially once you have large signatures with many defaults. I see no reason why all languages shouldn't adopt python-style foo(x, y, abc=123, xyz=456). DoThing(myflag=False) is generally much clearer than DoThing(False).

Polymorphism and inheritance
So a decent object system is a definite must for you?

What do yoy think of mostly-functional languages that add an OO library?

I haven't worked with a functional language professionally. Closest I came was with python which supports these constructs. I suppose it comes down to trade offs when designing a language and for what purpose you use them.
This is what I'd love to see die the most. All too often logic will be spread out through unmanageable inheritance trees. I've never seen a completely necessary use for inheritance and I've seen tonnes of misuse.
Inheritance is probably misused a lot. Having deeply nested inheritance structures is definitely a bad idea. It is useful in some situations.

Polymorphism on the other hand is a powerful technique to build flexible systems.

(comment deleted)
I'm not sure if you would consider this a feature of the language. But having a large community and large, readily available, libraries is huge.
Garbage collection or at least some sort of built in memory management that is consistent in all programs for that language.
(comment deleted)
For me list comprehensions are a godsend. I believe the loop logic is much better expressed in a single line of code than multiple lines of for or while syntax. No intermediate variables too. Now, this is becoming a common feature in all young languages, but the first time I encountered it in python a decade ago, I was on cloud 9. It is still one of my filter criteria when deciding if a language is worth investing in.
(comment deleted)
Generics. Can't cope with a bag of objects I need to cast and hope.
Composition (Interfaces) / Dependency injection / mocking.

Each one is invaluable on their own, but combined they make for very clean, maintainable, and easily testable software.

I find it hard to take any modern language that isn't capable of these seriously. For example, Rust (from talking to some Rust devs on /r/rust) does not have interfaces only "traits" and this prevents you from from being able to use DI thus limiting your ability to mock in the process.

And by dependency injection, I don't mean the XML bullshit that I see Java developers complaining about a lot.

Concurrency:

I know this can be put in via libraries, but it's so much easier when it's part of the language (or a language that's sufficiently expressive/flexible to allow it to not look bolted on). Actors, CSP, I don't care. The designs that fall out of using concurrency versus interleaving the logic are just better. They're often simpler, more likely to be correct, often delivering more reusable components. Erlang and Go being two modern exemplars of this, though utilizing different mechanisms.

Bit syntax:

Erlang's bit syntax, paired with binary comprehensions (like list comprehensions but over binary blobs), make a lot of my tasks (at least for prototyping since my employers won't use Erlang in final products) a lot easier. It's clean, paired with Erlang's unification/destructuring bind version of assignment to variables.

Pattern matching:

Remove all those if/else/if blocks, switch/case (or make them more effective) statements. Move a lot of decision points closer to where they actually need to be made (in function dispatch). Paired with static typing, this can also be used to enforce things like that functions are total functions.

Expressive type systems:

Ada, Pascal, Haskell, MLs in general. There are other languages that do a decent job, but these do it best. This allows you to spend time specifying your system, and then describe the system via your types. I'm not passing around an array of 4 bytes, I'm passing around an IP address. In Ada, I'm not passing around an integer, and if you do get smarter and name it something like "MessageIdentifier" it's not just an alias for Integer. You can constrain it to only hold values 0-15. You can make a type modular so that it correctly rolls over to 0, rather than indicating an error when you add 1 to the max value. This can be enforced (largely) at compile time, with some runtime checks. Via testing you can mitigate the need for the runtime checks and remove them if you want improved performance. But you have a smaller surface for runtime errors which greatly reduces burden on testing.

(Nearly) everything is an expression, few statements:

See lisps, erlang, really most functional languages. I find that this fits my mode of thinking and writing code much better. It can allow for some greater concision in code, though also potential for some really terrible looking constructs as well. Everything I write should have a return value of some sort. Even if expressions, switch/case blocks. Even if I choose to ignore it or put it into a throwaway variable like _.