Ideal programming language? What if we started from beginning having knowledge of current past? Throwing away “backwards-compability” to the furthest extends?
Many programming languages have their strengths and weaknesses. Guess what – it’s not only about features per se.
Functional? Type-safe? Immutability? Thread-safety? Pattern matching? It all does matter but it’s not enough to generally improve the process of creation and development.
What really matters in the end is the…
Efficiency of development
- the least amount of keystrokes to achieve a solution:
- speed of compilation
- for development, development and production
- speed of deployment
- possibly unnoticable by users
- possibly keeping the state of what’s working (for instance, server-side production apps having millions of active network connections)
- speed of testing
- performance per se in both manual and automatic testing
- adaptability to constant changes which is:
- good readability (it’s easy to understand written code by other developers)
- easiness of refactoring
- as little error-prone as possible
Improvements that actually do some damage
Multiple technologies introduce some solutions for problems by ignoring other solutions to other problems. This way we have beautiful language called Ruby which is slow and pretty bad for scalability. And multiple other things.
One of the easiest ways to save keystrokes is about not reinveinting wheels. Libraries. Modules. Plugins. And frameworks. Developers believe those things are necessary but I’ll make an argument against this.
node.js ecosystem brought the npm – a package manager featuring the Semantic Versioning. Due to many inconsitences this way of versioning became a lie. It’s not stable, not dependable – because of other developers who treat it subtly variously.
Semantic Versioning gave us information about changes. But took from us stability. It’s now hard to depend on other packages. Something called PATCH is treated too variously. In real world we are not interested whether latest version brings features, major or minor patches and bug fixes. Sometimes feature is a bug fix or bug fix has to bring a new incompatible feature. That’s life. Solution? Compatible Versioning.
To think more about libraries and dependencies I recommend to watch this “rant” over the state of development world:
Less error-prone language features
Functional languages come with immutability and pure functions (having no side effects).
The “immutability” argument often destroys having arrays. Functional languages often work mostly on lists. One-way linked lists, internally. Adding a new value to a list and keeping it immutable? It creates a new list. With linked lists it’s just about connecting a pointer (like pointers in C++) of wrapper for new element to old list’s latest element. That kills high performance. Of course, in many situations it could be optimised – due to AST analyze. If not, CPU couldn’t easly cache arrays. Probably pointers would be randomly spaced out in memory rather than placed in a row. Well, it depends on the memory allocation which is again – the language compiler’s work, as with AST optimization. However, it will probably break on lazy evaluation anyway (my guess and again – it depends on language design and compiler).
Pure function is a nice easily testable thing because anything it has access to is passed as an argument so it results in deterministic result and no modifications outside the function. Determinism is great for development and not always great for performance. Sometimes we need to globally modify something, then in fuctional languages we have architectures like FRP approaches similiar to CQRS where actions have to be sent and then interpreted somewhere else to just modify some global state. As you probably would guess it’s some kind of a performance issue.
Erlang or Elixir base on Actor Model which is all about immutability, copying state and failing (“let it fail” princinple) which is costly.
Other languages like Rust or Pony have this other cool feature – thread-safety. Sounds cool, huh? Yes but always comes with some cost! Lockless programming (which is very cool for high-performance software like computer games or other rendering software) may be impossible there.
Testing? Let’s test everything!
All those web front-end frameworks. Testing. There is Karma.JS, PhantomJS, headless browsers etc. Writing tests for it is everyday drama. Running them is part of it.
Let’s leave front-end. Backend is more cool here. Let’s have 1000 tests which run only logic, without any mention of rendering. Multiple things are mocked but unit tested. And then…
2 unit tests. 0 integration tests pic.twitter.com/V2Z9F4G1sJ
— The Practical Dev (@ThePracticalDev) 14 stycznia 2016
Eric Smith presented a clone (written in Clojure!) of game called Space Invaders that had over 100 tests and still couldn’t make it working in a very simple case. Although the topic of this video sounds outrageous, it’s worth to think about what he’s saying:
Basically, you have to think why and when you’ll need any kind of automatic testing. Sometimes it’s not worth the efforts.
Having strict types is having information. That enables compile-type type checking and helps future developer who doesn’t know or remember the code to not make any mistake while developing it.
If your languages doesn’t compile before going into runtime – it’s a dangerous toy.
Backward-compatibility is about maintaining peace in the world (so poeple would still want use computers) and business. Browsers are backward-compatible. Mobile devices are too (well, mostly). Operating systems. Did you know that some bugs exist because some software could be broken after fixing it?
And hey, programming languages are backward-compatible, too! Look at C++. C99, C14, C17. So many standards. So many ways to write better. So many ways to write the old way. Even Bjarne Stroustrup (creator of C++) stated that macros were a mistaken invention (I’ve seen him giving a lecture in Wrocław few years ago, declaring that).
Some groups are brave enough to break backward-compability in languages, such us Python (2.x to 3.x) or Elm (0.16 to 0.17). Guess what – multiple people are not satisfied. Because of libraries. Dependencies (oh, those again).
How to fix all that
You can’t. Take a deep breath. Create a new language from the ground up and make it perfect. But if you do that, then please make it easy and quick to build stuff with it.