Why would you choose Scripting?

Andrea Giammarchi
4 min readFeb 1, 2024
Photo by Hudson Hintze on Unsplash

This post is not about the boring JS vs TS debate, or Rust vs JS or anything like that, this post is about reasons I believe scripting will always win in the long term race.

Background

If we read the current entry in Wikipedia, the first “nay” we might face is this description:

Scripting languages are usually interpreted at runtime rather than compiled.

It’s been long time ago most scripting programming languages (PLs) passed through one or two layers of optimizations: the JIT and the “turbo-JIT” process, meaning that thinking about any modern scripting languages as inevitably slower, is not really the case anymore … and Yes, you can show me this or that benchmark that states otherwise, but here I am asking for a bit of an open mind, or feel free to skip this post already 👋

Moar Background …

When v8 landed as the new JS engine for any Chromium based browser, we didn’t see anyone else “just staring at it”, we witnessed instead Just in Time compilation happening for many other programming languages, as it was clear that optimization was both possible and worth it.

The Just in Time and further optimizations over it, though, work out of branching code, references, types, happening at runtime and what not … the unexpected value can reset optimizations and start from scratch, while repeated patterns would benefit from an almost “as native speed” execution any other time.

To some extend, the JIT and any further optimization of it, has been the AI behind scripting languages, and we do live in the AI era … don’t we?

Now be honest … please!

If you could ask the most advanced AI system ever to create a new programming language, would you ask for something that never fully understands your original intents and/or requires your (possibly) unexperienced or awkward attempts to write code, or asking for something extremely simple that can also run as native binary speed, with all the memory safety guards we want, and with none of the Rust (or similar) programming languages learning curve?

Would you then ask for “the easiest scripting language to date that has no footguns in it and can compile to native too”?

I don’t know about you, but I feel like the latter request would be more successful in the wild …

Impossible tasks

In programming, there is this divided world between sync and async code execution. In no PL you can simply ignore the async nature of a task, except those written from the scratch to make you feel, and your code look, like everything is just a natural sync flow of what you wrote.

CONTENT="$(curl -s https://anysite.com)"
echo $CONTENT

Now look at that! … a print like statement that wouldn’t care within its quotes what’s happening: it could be either sync or async but that result goes straight to the console, no matter what!

And no, I am not trying to tell you that Bash is the best scripting language out there, I am trying to tell you that from a PL user/developer point of view, it doesn’t matter if that curl operation is sync of async, all it matters is that it got assigned to a variable and such variable can be used later on anywhere!

Once async … always async …

A statically compiled PL that finds any branching code resulting into an async task, can’t possibly do anything different from translating the whole surrounding code as async:

function syncMaybe(id) {
if (!known.has(id))
known.set(id, fetch(`the-hell-out-of/${id}`).then(...));
return known.get(id);
}

This code clearely defines that syncMaybe callback as asynchronous, no need even define it as such, as an asynchronous operation is happening in one of its branches.

Now you repeat that code thousand times, where id is always known, you have actually a sync code to deal with, and a pattern to optimize for.

What would a native executable look like?

  • syncMaybe gotta always be resolved async, so anything using it gotta also be async in nature … hence slower by async defintion!

What would a scripting engine do instead?

  • syncMaybe ultimately run X times without branching out into that async case, we can actually inline it as long as the known reference actually holds that id and we know it … otherwise we should drop that inline execution and pass through the whole thing again

Can you see the difference? Nothing like this is a real-world concern or fact, because likely nothing like this has ever even been implemented … but on this note, the native use case has no solution to this, the code is shipped, it’s always going to be the slower path, as it’s going to be async no matter what, but the latter case, in future/smarter/AI driven engines can do the right thing: after 1000 runs and requests around the same IDs (think tables with data, emails, and so on) the code will be immediately responsive, but as long as any id is unknown, the code will start learning again or just populate its own table with that extra ID and keep going as fast as it can for every other request!

As Summary

This post is not targeting one PL vs another because the premises about how smart an engine could be are part of the unknowns this AI time could somehow bring to the table, but I do believe many of you would ask the sensible question to such AI so that scripting will have a way to be faster than native/upfront compiled targets and engines could get a chance to provide their best out of the unexpected entry all over the place.

Am I thinking too much ahead of time? It’s very possible, but once again, I think it was worth sharing my thoughts around this topic: Scripting will win, see you in 20 years or sooner when all the reality or industry proved me wrong 😇

--

--

Andrea Giammarchi

Web, Mobile, IoT, and all JS things since 00's. Formerly JS engineer at @nokia, @facebook, @twitter.