bytecodealliance/wac

Syntax for wasi-virt support

Opened this issue ยท 6 comments

WAC should have syntax for creating WASI-Virt instances so that they can be composed with other instances.

Some hypothetical expression syntax:

virtualize {
  fs: {
    mounts: {
       "/guest-path": "./host-path",
    },
    preopens: {
       "/guest-path": "./host-path",
    },
  },
  env: {
    vars: {
      "FOO": "BAR",
    },
    inherit: ["HOST-VAR"],
    // or: inherit: *,
  }
  // other WASI-Virt configuration here
}

which results in an instance that exports the requisite WASI interfaces (instances).

After some discussion with Luke just now, I think what's actually desirable is to do the inverse: instead of creating instances of a WASI-Virt component and feeding them through to any instantiations that make use of WASI, have an expression that crates a component from virtualizing another component.

So to revise the syntax:

let virt-foo = virtualize foo:bar {
   // Use WAVE-inspired syntax for WASI-Virt configuration here
};

where virt-foo is now a component that imports any non-WASI imports (or any WASI passthroughs depending on the virtualization configuration).

The component could then be used in new expressions:

let foo = new virt-foo {
   // Instantiation arguments here
};

Leaving this here as a reminder to revisit down the road once we flesh out more of the details of the total virtualize syntax:

During our WAC discussion today we brainstormed the idea of enabling virtualize to be "pluggable" in the future, e.g. virtualize command[foo:bar] { ... } where this implies that foo:bar will be virtualized using a registered adapter called command.

cc @lukewagner if you have any additional thoughts you'd like add.

Two partially-formed thoughts:

While several of the use cases we talked about involve virtualization, I expect there are others that don't, so we might not want to have virtualize be the keyword. So maybe then virtualize-command is the name of the component transformer and maybe we don't need a keyword prefix. So e.g. you could have:

let bar2 = virtualize-command[foo:bar] { ... };
let baz2 = virtualize-proxy[foo:baz] { ... };

I also wondered whether maybe the { ... } should be inside the [ ... ], since both foo:bar and the { ... } are arguments to the component transformer.

let bar2 = virtualize-command[foo:bar, { ... }];

This is normal if you think of it like a function call, but a little noisier than the preceding example. The latter might be useful if we imagine more-generalized arities for these component transformers, but I can't tell how likely that is.

This makes sense to me. My only concern would be the use of [ ] for the transformer expression given the existence of/ambiguity with named access expressions (e.g. foo-bar["baz"]).

Thinking about how to fit this into the current grammar, we might be able to amend postfix-expr in the following way to support a transformer-expr:

postfix-expr        ::= access-expr | named-access-expr | transformer-expr
transformer-expr    ::= '<' package-name ',' transformer-args '>'

Is <...> too jarring? Is there a better way to approach this?

cc/ @lukewagner @peterhuene

I'm not wed to [] and <> seem just about as good. @elliottt might also have thoughts.

Capturing the proposal for supporting "component transformers" in WAC from yesterday's meeting for posterity:

A component transform can be specified in WAC according to the following grammar:

transform ::= `transform` `<` package-name `>` package-name untyped-wave-record .

Where untyped-wave-record corresponds to the record production in the EBNF for wave

Additionally the transform rule defined above would be added to the wac grammar by alternating the let-statement rule, i.e:

let-statement ::= `let` id `=` (expr | transform) `;`

A rough sketch of an example:

let foobar = transform<wasi> foo:bar { 
    env: {
        vars: [("FOO": "BAR")],
        inherit: ["HOST-VAR"],
    }
   // other fields
}

The exact type of the untyped-wave-record is dependent upon the specific transformer being applied.

A future item discussed worth noting that i didn't mention above is to allow writing let config = untyped-wave-record and allowing "splatting" the contents of config into the untyped-record argument of a transform, e.g.:

let config = { env: { vars: [("FOO", "BAR")] }};
let foobar = transform<wasi> foo:bar { ..config }; 

Mostly documenting my understanding from yesterday so my weekend brain doesn't purge what we discussed but please let me know if i omitted anything cc/ @elliottt @peterhuene @lukewagner