component-model icon indicating copy to clipboard operation
component-model copied to clipboard

Is my mental model of the component-model correct?

Open badeend opened this issue 2 years ago • 3 comments

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.

?

badeend avatar Apr 03 '22 12:04 badeend

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.

lukewagner avatar Apr 04 '22 15:04 lukewagner

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

👍

badeend avatar Apr 04 '22 16:04 badeend

Great idea and yes, that'd be most welcome.

lukewagner avatar Apr 04 '22 17:04 lukewagner

Closing this issue since there's no remaining questions.

lukewagner avatar Aug 17 '22 16:08 lukewagner