On the other hand, optimized C++ is notoriously hard to debug, and debug C++ is slow (especially when using "zero overhead" abstractions that are only zero-overhead in release builds). There's cool work at reversible debugging, binary-patching, etc. (low-overhead tracepoints, Linux kernel Kprobes which failed to hook some functions I think were actually called, CONFIG_DYNAMIC_FTRACE, most recently https://justine.lol/ftrace/, though both CONFIG_DYNAMIC_FTRACE and ftrace depend on injecting nops into code which can later be replaced by tracing code).
JavaScript excels at neither performance nor dynamism, though I think it's still a lot more dynamic than C++ (userscripts can tamper with page contents and scripting) and faster than Python (through herculean effort of the V8 developers funded by Google ad revenue to make web apps and advertising faster). And C++/assembly, by its nature of allowing complete control over the machine, is difficult to sandbox beyond running code in an isolated process or VM (or compiling to WASM then to x86?), as opposed to JS/WASM being (theoretically) memory-safe and sandboxable by taking away APIs interfacing to the outside world.
Additionally, there is a specific kind of dominant "computing culture" that we live in. Show a new language to mainstream developers and they will want to know how to do a "hello world" and which text editor / command line program to run in order to use it, as if these are the only ways to interact with a machine in any meaningful way. Anything outside of this seems like it's "not real programming".
I don't see the break? "Clay" tooling exists for "industrial" code, but as secret sauce, and jig kludgery, and various other "making this more broadly available is not in/of our interest". "Clay"'s rejection of "industrial" has seemed more resource-starved exploit-not-explore group-think. Smalltalk/forth/etc audacious-scope rewrite-the-world efforts have seemed more "remake the world" than "to force us to remake ourselves". Try imaging a forth implementation effort that said "ok, we have bootstrap... so now the next obvious steps are supporting PICs and multiple dispatch and template jit, WAM and BEAM vms, linking Z3 solver, DHM type inference, ...". So perhaps rather than an inherent break between modes, and balancing to be done, there's a lack of available power, and of interests/resources aligned with ramping it.
But then, I'd like a programming environment which provides an powerful environment for making engineering tradeoffs, rather than ones which hardwire in very dramatic ones. I expect such an environment, when it finally exists, won't be hard to recognize. As, for example, basic reimplementation of existing modern languages, with their big test suites, libraries, community repos, specs sometimes transliteratable directly into code, code-as-documentation and highly-investment-in optimization, is a natural forcing-factor exercise for such an environment. So when you see a small team spewing new language implementations... maybe we've at long last hit phase transition. And if one can't easily manage that, in bulk, even with all that leverage... then it's not a very powerful environment, is it?
Granted, if you ignore one axis and optimize the heck out of the other one, you're unlikely to end up in an optimal region of the ignored axis. But there are optimization paths that move in good directions on both axes at once, until you reach some limit where you sort of move around on some boundary arc by trading off one against the other.
Apple's Dylan project was conceived with the explicit purpose of developing a language and runtime that would satisfy the highly-interactive-programming enthusiasts (that is, the Smalltalkers and Lispers) in Apple's ATG and other researchy groups, while also producing built artifacts that were fast and interop-friendly and compact enough that they wouldn't all have to be rewritten from scratch in C, Pascal, or assembly before the product groups would agree to ship them.
The Dylan team did a pretty good job. I was one of their internal customers, working on an experimental handheld OS written mostly in Dylan.
The Dylan team gave us a modified version of Macintosh Common Lisp, called Leibniz, with Dylan support. Leibniz was just MCL, but with a Dylan compiler, object system, and runtime built into it. The Dylan compiler cross-compiled to the handheld's hardware. Our dev machines were Macs, either with daughterboards stuck into nubus slots, or with actual handheld hardware ribbon-cabled to the nubus.
Leibniz had a second version of everything in the MCL environment: there were the normal Lisp listener windows, but also Dylan listener windows. There were normal Lisp editor windows and Dylan editor windows. And so on.
Code compiled and evaluated in the Lisp windows ran on the Mac hardware. Code compiled and evaluated in the Dylan windows ran on the handheld hardware.
Our built software ran on the same handheld hardware as the C++ OS. It performed well--well enough to make some of the C++ team curious sometimes about how we did certain things.
The relevant point is that you can make a highly-interactive and malleable language and development environment that also delivers fast, compact artifacts. I don't think there's an irresolvable tension between those two goals.
On the other hand, optimizing toward both goals is more work than optimizing one at the expense of the other. If you elevate one goal above the other, the elevated goal will benefit and the other will suffer. Optimizing both means paying attention to both all the time, and that sometimes means that you will disqualify certain options. Optimizing both shrinks the workable solution space, so you have to look harder and sometimes solve problems in less easy ways than you would if you were thinking only about the one goal.
On top of that, the features that make an environment a great, malleable, highly-interactive environment are extra. You still have to do all the same kind of work that you need if you don't care about making a highly-interactive environment--lexers, parsers, compilers, optimizers, linkers, editors, indexers, and so on, and so forth--but on top of that, you have to design and build all of the features that make an environment highly interactive--a repl that has visibility into everything in the runtime as it runs, error handling that can spin up an interactive session in the dynamic context of a signaled error, runtime facilities that detect and keep track of every dependency that changes dynamically and knows what to do about it, inspectors that know how to expose and edit every element of state in the whole system, and so on.
That's really the obstacle, I think: a really malleable environment is just a lot more work. That, and to build one you need builders who know what they are and how to build them.
"Smalltalk in a C world (2013)" https://news.ycombinator.com/item?id=31462735
Not entirely sure about the status of the project, but there appear to have been a few updates in github more recently than the news section of the website.
such is life, it's gonna popup again in php10 or typescript5 for sure
>= Pharo 9: Git (through something called Iceberg)
From your clean running image, you open the Repository Browser window. You load app code from the repository, make changes etc., and commit back to the shared repository.
There were many options for organizing “packages” and creating lists of packages and version numbers for a “build”. Also different options for ownership of code, locking code from changes, difference reports, etc.
Open source tools I have not used.
Web apps are too dominant, and the fact is much of backend engineering is mildly unsuitable for a dynamic language runtime due to raw performance.
I think that's the issue. No golden use case like Ruby on Rails.
If Apple supported Smalltalk for native iOS apps things would be fine. But pigs would fly first.
I’d be curious to know more about this!