component-model
component-model copied to clipboard
Is my mental model of the component-model correct?
I think I've seen the following analogies briefly mentioned somewhere, though I don't remember where. Are these correct?
Instances
An "instance" is a Dictionary/Map/Record object, mapping exported names to exported "things". The definition of "thing" depends on the kind of instance:
For module instances they can be:
- "core" functions a.k.a. "lowered" functions,
- tables,
- memories,
- globals.
For component instances they can be:
- "component-model" functions a.k.a. "lifted" functions,
- values of interface types,
- interface types themselves. (These seem to be only exportable, but not importable. Is that correct?)
- Modules
- Components
- Component instances
Components
A "component" is the definition of a function that is executed at link-time, taking the imports as its arguments and returning an component instance.
Keyword | Analogy |
---|---|
import |
A parameter of the function. This can be anything |
export |
An entry in the to-be-returned component instance. |
instantiate |
Invoking a component function. |
with |
Named argument. |
All other direct children of (component ...) |
Various kinds of let bindings within the function body. |
If preceding analogy is correct, do these:
examples from the explainer
(component
(import "name" (value $name string))
(import "libc" (module $Libc
(export "memory" (memory 1))
(export "realloc" (func (param i32 i32 i32 i32) (result i32)))
(export "free" (func (param i32 i32 i32)))
))
(instance $libc (instantiate (module $Libc)))
(module $Main
(import "libc" ...)
(func (export "start") (param i32 i32) (result i32 i32)
... general-purpose compute
)
)
(instance $main (instantiate (module $Main) (with "libc" (instance $libc))))
(func $start
(canon.lift (func (param string) (result string)) (into $libc) (func $main "start"))
)
(start $start (value $name) (result (value $greeting)))
(export "greeting" (value $greeting))
)
(component
(import "wasi:logging" (instance $logging
(export "log" (func (param string)))
))
(import "libc" (module $Libc
(export "memory" (memory 1))
(export "realloc" (func (param i32 i32) (result i32)))
(export "free" (func (param i32)))
))
(instance $libc (instantiate (module $Libc)))
(func $log
(canon.lower (into $libc) (func $logging "log"))
)
(module $Main
(import "libc" "memory" (memory 1))
(import "libc" "realloc" (func (param i32 i32) (result i32)))
(import "libc" "free" (func (param i32)))
(import "wasi:logging" "log" (func $log (param i32 i32)))
(func (export "run") (param i32 i32) (result i32 i32)
... (call $log) ...
)
)
(instance $main (instantiate (module $Main)
(with "libc" (instance $libc))
(with "wasi:logging" (instance (export "log" (func $log))))
))
(func (export "run")
(canon.lift (func (param string) (result string)) (into $libc) (func $main "run"))
)
)
translate into the following:
function-like equivalent
(linker-func
(param "name" $name (value string))
(param "libc" $Libc (module
(export "memory" (memory 1))
(export "realloc" (func (param i32 i32 i32 i32) (result i32)))
(export "free" (func (param i32 i32 i32)))
))
(let $libc (instantiate (module $Libc)))
(let $Main (module
(import "libc" ...)
(func (export "start") (param i32 i32) (result i32 i32)
... general-purpose compute
)
))
(let $main (instantiate (module $Main) (with "libc" (instance $libc))))
(let $start (canon.lift (func (param string) (result string)) (into $libc) (func $main "start")))
(let $greeting (start $start (value $name)))
(return (instance
(export "greeting" (value $greeting))
))
)
(linker-func
(param "wasi:logging" $logging (instance
(export "log" (func (param string)))
))
(param "libc" $Libc (module
(export "memory" (memory 1))
(export "realloc" (func (param i32 i32) (result i32)))
(export "free" (func (param i32)))
))
(let $libc (instantiate (module $Libc)))
(let $log (canon.lower (into $libc) (func $logging "log")))
(let $Main (module
(import "libc" "memory" (memory 1))
(import "libc" "realloc" (func (param i32 i32) (result i32)))
(import "libc" "free" (func (param i32)))
(import "wasi:logging" "log" (func $log (param i32 i32)))
(func (export "run") (param i32 i32) (result i32 i32)
... (call $log) ...
)
))
(let $main (instantiate (module $Main)
(with "libc" (instance $libc))
(with "wasi:logging" (instance (export "log" (func $log))))
))
(return (instance
(export "run" (canon.lift (func (param string) (result string)) (into $libc) (func $main "run")))
))
)
Legend:
linker-func ::= (linker-func <param>* <let>* <return>)
param ::= (param <import-name> <local-binding-id> <type>)
let ::= (let <local-binding-id> <expression>)
return ::= (return <expression>)
expression ::= // Everything else.
?
Great writeup! "Yes", you're right on. What you're describing is also how, iiuc, ML modules ("functors") are represented.
* interface types themselves. (These seem to be only exportable, but not importable. Is that correct?)
That is just a temporary artifact of the incremental way that this spec is being assembled (we need type exports right now for wit-bindgen but type imports not quite yet). But the current intention is that that component-model MVP will have both type imports and exports, with both being made more "interesting" by the addition of resource types, which are effectively the component-model's lifting of the core wasm type imports proposal.
If you're interested to know what the addition of types imports/exports means in a "functional" interpretation like you've given here, see F-ing Modules. Andreas also has a sketch of an "F-ing" elaboration tailored to components that we'll be adding in a future PR along with the resource types.
Great writeup! "Yes", you're right on.
Nice. Thanks for confirming!
The explainer gets into the weeds pretty quickly. To me, it would have been helpful if there was some high-level overview explaining these semantics before diving into the details. Many things in the explainer now suddenly *click * into place.
Would you mind me opening a PR adding such a section?
the current intention is that that component-model MVP will have both type imports and exports
👍
Great idea and yes, that'd be most welcome.
Closing this issue since there's no remaining questions.