Coder Social home page Coder Social logo

proposals's Introduction

Proposals

This repo contains any Extism Improvement Proposal, and should follow the format of:

EIP-NNN-name-of-proposal.md

Submitting a Proposal

Anyone can submit a proposal, and this is the recommended way to begin any work you'd like to contribute, or to suggest a change or feature.

Steps:

  1. Open an issue with a high-level idea of what the improvement or change would include and why.
  2. Extism members will engage with you about the proposal and will create the EIP, for further collaboration.
  3. The new EIP will act as a living document for specification.

Table of Contents

  1. Plugin calling convention
  2. Module namespace for host functions
  3. Language-specific library repo management

proposals's People

Contributors

bhelx avatar nilslice avatar zshipko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

proposals's Issues

EIP: Language-Specific Extism Docker Images

What

I propose providing language-specific Docker images for users to build on top of for easy deployment.

Why

Docker is obviously the predominant container platform currently. And with the cloud being the hype, and with Extism always gaining more traction every day, I can see users wishing to Dockerize an Extism host application to deploy via Docker Compose, Kubernetes+Helm Charts, etc. Whenever users have to have binaries already installed for their applications to run and are already considering Dockerizing it, they often opt for a Docker image with the bare minimum of what they need (often just a language runtime) for ease of use as long as the resulting image is small enough to satisfy the users' needs; especially if there is an official one vendored by the company/foundation/group that provides the binary (see ffmpeg's Docker image).

Proposed specs:

  • Built on top of Alpine/Scratch image to keep image small
  • Two different image options,
    1. one with only Extism pre-installed (possibly named extism-lite-<semver_number>)
    2. another with the language runtime (or other necessary binaries/files/etc.) as well as Extism pre-installed (possibly named extism-<semver_number>-<language_name>-<semver_number>)
  • Language-specific images should be maintained and updated for the last 3 major releases for backwards compatibility (if compatible with Extism version), after which, routine maintenance and updating is stopped and only performed whenever critical issues are opened
  • Implemented and maintained inside of language specific SDK repos, or in central repo specifically for Docker images for ease of maintainability

Protocol definition with language-specific idiomatic bindings

This is a feature request to add wit-like protocol definition, from which language-specific bindings with correct function names and signatures can be generated.

Wit comparison

Let's consider a simple example: the protocol will export a function called "calculate", and the host will provide functions "get_x" and "get_y" that the plugin can call during the calculation.

The wit protocol would look something like this:

package example:protocol

world my-world {
  import get-x: func() -> float64
  import get-y: func() -> float64

  export run: calculate() -> float64
}

Plugin side

Currently when writing a Rust plugin, we have to manually import the "get_x" and "get_y" functions, and we have to manually export a function "calculate" with manually created signature.

Instead, extism could generate an idiomatic "binding" based on the protocol, in case of Rust, a trait that looks something like this:

trait MyWorld {
    fn calculate() -> f64;
}

In addition, it could define the external "get_x" and "get_y" functions with correct Rust signature:

fn get_x() -> f64 {
     // call the extern function
}
fn get_y() -> f64 {
     // call the extern function
}

All we would have to do is implement the trait, for example:

struct MyPlugin;
impl MyWorld for MyPlugin {
    fn calculate() -> f64 {
        get_x() + get_y()
    }
}

And done.

Host side

Similarly, when writing a rust host for example, we currently have to specify the name of the function we want to call as string, and pass arguments "dynamically" as a slice of extism::Val. When defining the imported functions, the experience is similar.

Instead, we could generate idiomatic function signatures like so:

trait MyWorldImports {
    fn get_x() -> f64;
    fn get_y() -> f64;
}
struct MyWorldInstance<T> { ... }
impl<T: MyWorldImports> MyWorldInstance<T>{
    fn instantiate(module_bytes: &[u8] import_object: T) -> Result<Self> { ... }
    fn calculate() -> f64 { ... }
}

So instead of relying on manually typing out function names and using dynamic signatures, we get a nice trait where we implement the get_x and get_y method with correct type signatures, and we get an instance from instantiate where we can call the calculate method.

Additional notes

I used a lot of simplification in the code examples, but my basis was this project where I tried to use wit-bindgen, but it didn't work because of some error with the component model version.

More importantly, having idiomatic bindings like this is not only better developer experience to use, but also (and perhaps more importantly), when we change the protocol, we get syntax errors instead of runtime errors in places where we forget to update the code.

And of course, add these idiomatic bindings for every language, not just Rust. For example, in Java/C#, use interface instead of a trait, etc.

What format to use

If you decide that this is something you want for extism, then next question would be what protocol format to use. One option would be to generate the host bindings automatically from the plugin code, or vice versa, but I do not think this is the right solution.

Using .wit format directly as-is might also not be optimal - since I'm not sure if extism supports all features of wit, like resources. Also, if you are using the wit format anyway, then you might as well use wit-bindgen instead.

I think a best solution would be to create a new protocol format designed for extism.

EIP: calling user defined functions from SDK host

Say a user uses the Go SDK and wants to "share" a Go function from their app to a plugin, making it an import to said plugin.

func logMessage(msg string) {
  log.Println("from plugin:", msg)
}

func main() {
  ext := extism.New()
  plugin := ext.LoadPlugin(...)

  // provide a handle to the function, adding some detail about 
  // what function to call, the "namespace" (module namespace 
  // to import from in wasm), the function name within wasm, and 
  // its params/returns
  plugin.AddFunc(logMessage, "AcmeAppLib", "log",  ...params) // tbd, how to declare the params/returns here



  out, err := plugin.Call(...)
  // ...
}

Easy WASI Command Runner

To continue expanding Extism from a plugin system to a framework, we could add a WASI command module runner. A WASI command module runner would allow using Extism to run existing programs in the wild in addition to plugins built with the various PDKs.

The interface would take in a buffer to use as STDIN data and return two buffers out, STDOUT and STDERR. Setting this up in all WASM runtimes is non-trivial. Such interface would be convenient for creating a library function around a command module for example.

Prototype implementation: https://github.com/extism/extism/tree/g4vi/wasi-command-runner

Update EIP-001 to match implementation

re: #1, tracking the need to update the document so that it's accurate as to the implementation. in near-future, we should be able to point people to these EIPs for extended documentation, and be able to copy/paste from them into official docs.

Provide historical context around EIPs

Could there be a table added to the EIPs with a change log and an acceptance / merge date / PR references?

It's getting confusing because some EIPs are already accepted and implemented (for example, optional contexts or the monolithic repo). Then there are other EIPs like breaking the monorepo down, which doesn't really make sense next to the monorepo EIP, because you can't see that one is accepted and one is planned.

EIP 005: Versioning System

I think it’s a good idea to split host API and plug-in API compatibility like this and version them separately. the plug-in compatibility is gonna be the real bear, because releasing a new version of your extism-using product will break all existing plugins if it versions up the runtime to a plug-in breaking version, which is a shame. The host SDK and runtime compatibility are almost not an issue since it’s all on the developer side (but nice to think about them anyway :)) . If you’re not careful though you could easily get into the Python 2 to 3 situation on the PDK side.

One thing you could consider is how the plug-in API could be designed to have escape hatches so future runtimes could emulate compatibility and keep older versions working if they desired. eg. If I could have a blue-oyster that’s latest of all versions, but I could have a (probably bigger, slower) version like blue-oyster_barnacle that supported 2 pdk versions, I would definitely use that. That would let greenfield apps choose not to support legacy plugin coding models, but brownfield could keep running some backwards-compatible version.

An example from the Python 2 to 3 was converting print from a statement to a function (print “a” vs print(“a”). From a language design perspective they wanted to make this change. However, it wasn’t truly breaking - they could have supported both forms if they really wanted to, or allow the runtime to run in a “compatibility mode”. I do think this should be opt-in though, not required for all users.

You could also introduce a layer of indirection and develop the code as requested capabilities and supported capabilities instead (taking your example from 2 to n). Then each logical area of the code could be its own version - like when cancellable plugins were introduced. Then the PDK can just suggest to the runtime which capabilities it needs, instead of hardcoding the comparison to the color-creature combo.
That would have a benefit too that you could not even require upgrades of plugins that didn’t make use of capabilities that had breaking changes
I think your solution is probably sufficient or close though, just throwing some ideas out

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.