Using Swift as a embedded scripting language for a macOS App?

Is there a way to use Swift script files as an embedded scripting language within a macOS app?

The idea is to allow users to write their own Swift-based scripts to control the app’s behavior.

Background:

Hammerspoon uses Lua as its embedded scripting language. I’m wondering whether it’s possible to replace Lua with Swift for user scripting — similar to how JavaScriptCore enables JavaScript scripting.

someone tried this: Demo of my Swift Interpreter that uses SwiftSyntax but this is done by an Swift Interpreter (closed sourse) which parse the swift to AST.

If in your case it's acceptable for script authors to compile Swift code to Wasm before executing their scripts, I wonder if WasmKit could be a solution?

The AudioWorkstation example in the swiftlang/swift-for-wasm-examples repository demonstrates a Hummingbird server that loads and executes audio plugins written in Swift. This example project was demoed at SwiftLeeds, so you can check out the recorded talk to see it an action.

WasmKit doesn't require a server of course, it's an interpreter (no JIT) that can run on any platform supported by Swift.

5 Likes

Thank you for this talk, @Max_Desiatov, it was quite illuminating!

This would allow one to develop widely multi-platform apps with a plugin-based architecture that can even run on restricted platforms like iOS and (within plugin API limitations) support any language at the discretion of the plugin author, as well as come with built-in security precautions, without forcing one to rely on OS-specific solutions like XPC and process sandboxing.

The base assumption for this would be that each target OS has a highly optimized WASM interpreter, which would be the basis for the performance cost of such an app architecture.

1 Like

Apple’s ExtensionKit is one way to go about this.

1 Like

This is a critical point IMO. Lua can execute code directly. You can do the same thing with JavaScriptCore. To get this facility in Swift / Wasm you’d need to embed the Swift compiler. That’s certainly doable, but it’s also not trivial.

FWIW this recently came up on DevForums, and you can see my reply there.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

I was always under the impression that the REPL uses some kind of JIT. Is that true and would it be possible to use for interpreting Swift scripts?

Swift REPL is an LLDB wrapper. Think of typing print("foo") in the REPL as typing po print("foo") in LLDB.

2 Likes

You could also think about using Apples new containerization framework to ship the Swift toolchain that you need to interpret or compile the user script to WASM via a container that contains the Swift compiler. Obviously not a light-weight approach.

1 Like

Also keep in mind that a stripped swift-frontend binary is 125Mb or so, which is probably quite a bit larger than some of the other scripting engines out there.

2 Likes

A bit off topic, but are there statically typed languages that could be used as a scripting language without hassle?

1 Like

Of course, Nelua and TypeScript are just few examples. Whether a language is statically or dynamically typed is orthogonal to scripting, it's the presence of an embeddable interpreter (usually backed by a bytecode VM or JIT for performance) that makes it work.

Here's Jane Street sharing their experience of using OCaml for scripting.

2 Likes

But that would raise a problem of bringing toolchain and etc., no? Even for Ocaml there are topics like that, so not sure it's without hassle.

It's an implementation problem and conventions/definitions problem.

Why would someone call the TypeScript parser and type checker a toolchain, but then wouldn't call CPython a toolchain? Surely you can't make a distinction whether it's a library or not, because all of these can be used as libraries. Python supports advanced type annotations with generics since 3.8-3.9, you can write scripts as if it's a statically typed language and be fine with that. After all, type checking is first of all useful when writing code, when it's executed the type annotations are stripped out and machine code operates on raw bytes. You don't necessarily need a type checker to execute a script that was previously verified/compiled by its author.

Even the definition of "scripting" itself is incredibly murky and IMO overused.

Bringing this back on topic, I don't see why in theory Swift couldn't be used as a language for plugins, while it would require some improvements to make that easier. Yes, the toolchain currently can't be easily used as a library and toolchain binaries are too big for easy embedding. But it's a limitation of how things are currently done, not something that the language itself can't allow.

3 Likes

I‘m not like arguing and completely agree, more like wondering—even for Typescript I don’t see it used often. Guess the problem is not making the language scriptable per se, more likely with dynamic languages you have faster feedback loop. Or simply people used to associate some languages as scripting?

But anyway happy to here there could be improvements done. :slightly_smiling_face: