Re:Ask HN: What programming feature can't you live without?
by Jtsummers on Wednesday May 03, 2017 @11:57AM
(#14256890)
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 _.
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 _.