Programming language versioning bothers me
rachelbythebay.com
rachelbythebay.com
Programming languages making new revisions is better than the absence of revisions.
> All of this would be different if you could just compile this stuff to a binary format
Hardly. C99/11, C++98/03/11/14/17/2a -- these changes modify syntactical language features available during compilation and target libs. This article is from 2012 but its author appears to come to this conclusion as of yesterday [1]. Unfortunately, instead of embracing the change that takes place in both Python and C/C++, she seems to reject it all due to the complexity introduced.
Indeed, some folks tend to embrace portability over all else. CPython itself only started to move beyond requiring C89 within the last couple of years, and they're selective on which C99 features they're willing to leverage. It's an extraordinarily portable project, IMO. OTOH there are projects who leverage more modern language features in C or C++. Those projects sacrifice portability, often to move the complexity of some of their design elements into the compiler and/or platform libraries. I believe that the latter is a better option, when it's available.
Expanding on an aspect of that...
Usually portability, along with many other such things, should be considered as a higher level goal. Decide on the portability requirements and then pick the best options within constraints. This varies wildly between projects! The same dev team may come to different decisions on various projects.
is it, though ? I write daily in C++17 and have yet to find a relevant platform where a pure C89 program such as CPython would work and mine wouldn't. I mean, for hell's sake, you can target Commodore 64 and MS-DOS with the latest c++ revisions.
With C you are usually delivering a compiled binary executable. If they did add a feature to C, you could use the new compiler with the new feature on your local development environment. It wouldn't matter at all to the place you deployed it, as long as you make a compatible binary.
There do exist some tools to build binaries from Python, and other interpreted languages. They still need a lot of work. I would say that it should even be a part of python itself. I see no reason that I shouldn't be able to do something like
python --compile hello.py > hello
./hello
"Hello World"
Or on Windows python --compile hello.py > hello.exe
Now the target environment doesn't even need Python to be installed at all!| With C you are usually delivering a compiled binary executable. If
| they did add a feature to C, you could use the new compiler on your
| local development environment, and it wouldn't matter at all to the
| place you deployed it as long as you delivery a compatible binary.
`----
with C probably not, which has not been accumulating features for like forever. C++ however, is another beast.
some machines might not have new fangled c++11 stuff installed on them. sources that use these features, might have a tough time. as an example: c++11 introduced unique_ptr to the world. if you try to use that, you might notice that not all of c++ gets compiled into the binary. some of it e.g. libstdc++ lives on the disk as well, and gets linked during runtime. this can now be fixed via some '-rpath' incantations or '-static' linking (which increases the size etc.) perhaps then you might start looking at 'LD_LIBRARY_PATH' ?
what about creating your own little sandboxes e.g. via chroot's ? but if you step back a bit, all this boils down to is creating little environments within which programs, which are supposed to be running from command-line are now sequestered...
what a grate mess :)
Edit: Is there a python equivalent of tclquadcode, https://core.tcl.tk/tclquadcode/dir?ci=tip - which should compile tcl into llvm's IR?
You're mistaken. C has added features to its standard library before. It allows you to detect the presence of this feature at compile-time with a preprocessor definition. But if your compiler supports the feature and your target does not, in most cases you will get a fatal error regarding symbol resolution from the loader.
This problem is remarkably similar to the one faced by Python developers. The difference is that Python's philosophy includes a statement regarding "batteries included", so its standard library has taken on more frequent additions than C's.
In order to portably target multiple environments, you will occasionally need to handle ImportError (disable a product feature or have a fallback implementation) or (in C, e.g.) provide a fallback implementation designated as "weak". Or forego portability, capitalize on the feature, and raise the requirements for your target.
Or are using .dll or .so files?
For a static C program, regarding compiling and linking as part of the same (build) process, I see only two possible outcomes. Either I get an error at build time, or I get an executable that I can drop on the target that will run.
The one exception I can see to this is if the program is going to make an OS call that some versions of the OS don't support.
No, I meant from the dynamic loader (like ld.so).
> Or are using .dll or .so files?
Yes, that's a very common idiom for libc and libstdc++/libc++. Yes, you're right that statically linked binaries will not suffer this dependency and usually end up more portable (often at a cost of some memory and security/maintenance).
> The one exception I can see to this is if the program is going to make an OS call that some versions of the OS don't support.
Exactly. This is actually a little less rare than C library features, from what I've seen. But, here, like ImportError and providing weak impls, you can handle ENOSYS.
I do wish that there'd been a newer standardisation effort since, for a couple of reason, but it's really, really nice that the people haven't had to waste effort updating working code to deal with language changes.
The feature pragma (see https://perldoc.perl.org/feature.html) lets you manage new features on an individual basis. You just write: feature NAME_OF_FEATURE;. For example, feature signatures; In this case adding subroutine/method signature support. This is a compile time check.
If you don't want to hassle with enabling features on a piecemeal basis, you can also specify a version of perl you wish to rely on with use VERSION (see https://perldoc.perl.org/functions/use.html). For example, if want all of the standard features of perl 5.26.1, you say use v5.26.1.
Also databases, caches, REST APIs, syscalls (e.g. libc), docker, dependent libraries, browsers and much much more....
In spite of that I don't see it as being an intrinsically difficult problem to solve. Just make a best effort to run the same versions of stuff in dev and test as you do in prod (with python, pyenv and dependency pinning is your friend here) and make liberal use of sanity checks - if it's only tested on versions x->y, put in a check that fails with a message if it sees anything else.
The main problem isn't that ^^ that is hard, it's just that nobody really does it or recognizes how much time can be saved by doing it.
Dependency management for software you're distributing is a hassle, to be sure. The version of the language is one of many considerations. System calls, memory constraints, paths, and many other things can differ from one environment to another.
That's no different than any other interpreted language...
The reason this is so natural for Clojure is that it's a hosted language; the standard library & language itself is decoupled from the runtime platform (JVM).
With Clojure, it's actually funny to think that I have projects on my hard drive that use 3-4 different versions of clojure (not even counting various versions of clojurescript!), without any issues. Doing this with, say, Node.js requires a lot of extra ceremony.
Clojure(script) benefits as a compiled language as well. If Clojure 2.0 suddenly discovered Postfix notation was the GREATEST THING EVER, my 1.6, 1.8, and 1.9 projects and JARs are entirely unaffected. They could even run side by side, from the same interpreter version, without so much as a shrug of concern.
> (not even counting various versions of clojurescript!)
That made me laugh! Clojurescript should use a date stamp as the incremental version.
Her argument seems to say 'coming from c' which suggests she didn't experience difference in handling of ints, shorts, signed and unsigned, alignment, #pragmas. C on SCO UNIX was not identical to C on an Apollo domain. It might have been one C standard (K&R) before ANSI C, but it was not one implementation and 'language features' are not at root that different to coding to compiler behaviour.
Oh.. getopt() wars...
I guess though, I have the benefit of checked types. When using this same strategy with an interpreted language, you're definitely leaning hard on your test coverage.
You query the language runtime for the version and use it as an assert. It's standard everywhere, except in the web where polyfills are still a thing.