aboutsummaryrefslogtreecommitdiffstats
path: root/core/plugin-guide.md
diff options
context:
space:
mode:
Diffstat (limited to 'core/plugin-guide.md')
-rw-r--r--core/plugin-guide.md111
1 files changed, 79 insertions, 32 deletions
diff --git a/core/plugin-guide.md b/core/plugin-guide.md
index f5e3c50..1086452 100644
--- a/core/plugin-guide.md
+++ b/core/plugin-guide.md
@@ -11,13 +11,13 @@ are exported in the `base` module.
## documentation metadata
The lua module should return a `Scope` or a table that will be converted using
-`Scope.from_table`. All exports should be documented using `Value.meta`, which
-attaches a `meta` table to the value that is used for error messages,
+`Scope.from_table`. All exports should be documented using `ValueStream.meta`,
+which attaches a `meta` table to the value that is used for error messages,
documentation generation and [`(doc)`][builtins-doc].
- import Value from require 'core.base'
+ import ValueStream from require 'core.base'
- two = Value.meta
+ two = ValueStream.meta
meta:
name: 'two'
summary: "the number two"
@@ -43,30 +43,31 @@ Most plugins will want to define a number of *Op*s to be used by the user. They
are implemented by deriving from the `Op` class and implementing at least the
`Op:setup` and `Op:tick` methods.
- import Value, Op, Input, match from require 'core.base'
+ import ValueStream, Op, Input, match from require 'core.base'
- my_add = Value.meta
+ total_sum = ValueStream.meta
meta:
- name: 'my-add'
- summary: "Add two numbers."
- examples: { '(my-add a b)' }
- description: "Add two numbers, plugin-style."
+ name: 'total-sum'
+ summary: "Keep a total of incoming numbers."
+ examples: { '(total-sum num!)' }
+ description: "Keep a total sum of incoming number events, plugin-style."
value: class extends Op
- new: => super 'num'
-
+ new: (...) =>
+ super ...
+ @state or= { total: 0 }
+ @out or= ValueStream 'num', @state.total
+
setup: (inputs, scope) =>
- { a, b } = match 'num num', inputs
+ { num } = match 'num!', inputs
- super
- a: Inputs.value a
- b: Inputs.value b
+ super num: Inputs.hot num
tick: =>
- @out\set @inputs.a! + @inputs.b!
-
+ @state.total += @inputs.num!
+ @out\set @state.total
{
- 'my-add': my_add
+ 'total-sum': total_sum
}
### Op:setup
@@ -86,9 +87,9 @@ argument types and matches them against the provided arguments:
{ str, numbers, optional } = match 'str *num any?', inputs
`match` matches arguments greedily from left to right. Each part of the string
-is the type-name of a Value. Parts can be optional (`num?`), multiple (`*num` -
-one or more numbers) or both (`*num?` - zero or more numbers). If there is an
-equals sign in front of a part, the corresponding `Result` has to be
+is the type-name of an argument. Parts can be optional (`num?`), multiple
+(`*num` - one or more numbers) or both (`*num?` - zero or more numbers). If
+there is an equals sign in front of a part, the corresponding `Result` has to be
*evaltime constant*. The special typename `any` can be used for generic Ops.
If there are more complex dependencies between arguments, it is recommended to
@@ -97,17 +98,67 @@ manually. For invalid or missing arguments, `Error` instances should be thrown
using `error` or `assert`.
#### input setup
-
-(section wip since changes are anticipated)
+There are two types of inputs: `Input.hot` and `Input.cold`:
+
+*Cold* inputs do not cause the Op to update when changes to the input stream
+are made. They are useful to 'ignore' changes to inputs which are only relevant
+when another input changed value. Imagine for example a `send-value-when` Op,
+which sends a value only when a `bang!` input is live. This Op doesn't have to
+update when the value changes, it's enough to update only when the trigger input
+changes and simply read the value in that moment.
+
+*Hot* inputs on the other hand mark the input stream as a dependency for the
+Op. Depending on the type of `Stream`, the semantics are a little different:
+
+- For `ValueStream`s, the Op updates whenever the current value changes. When
+ an input stream is swapped out for another one at evaltime, but their values
+ are momentarily equal, the input is not considered dirty.
+- For `EventStream`s and `IOStream`s, the Op updates whenever the stream is
+ dirty. There is no special handling when the stream is swapped out at
+ evaltime.
+
+All `Result`s from the `inputs` argument that are taken into consideration
+should be wrapped in an `Input` instance using either `Input.hot` or
+`Input.cold`, and need to be passed to the `Op:setup` super implementation.
+To illustrate with the `send-value-when` example:
+
+ setup: (inputs, scope) =>
+ { trig, value } = match 'bang! any', inputs
+
+ super
+ trig: Inputs.hot trig
+ value: Inputs.cold value
+
+`Op:setup` takes a table that can have any (even nested) shape you want, as
+long as all 'leaf values' are `Input` instances. The following are both valid:
+
+ super { (Inputs.hot trig), (Inputs.cold value) }
+
+ super
+ trigger:Inputs.hot trig
+ values: { (Inputs.cold val0), (Inputs.cold val1), (Inputs.cold val2) }
#### output setup
-
-When `Op:setup` finishes, `@out` has to be set to a `Value` instance. The
+When `Op:setup` finishes, `@out` has to be set to a `Stream` instance. The
instance can be created in `Op:setup`, or by overriding the constructor and
-delegating to the original one using `super`. In general this way of creating
-the output value is preferred, and it is only moved to `Op:setup` if the output
+delegating to the original one using `super`. In general setting it in the
+constructor is preferred, and it is only moved to `Op:setup` if the output
type depends on the arguments received.
+There are three types of `Stream`s that can be created:
+
+- `ValueStream`s track *continuous values*. They can only have one value per
+ tick, and downstream Ops will not update when a *ValueStream* has been set
+ to the same value it already had. They are updated using `ValueStream:set`.
+- `EventStream`s transmit *momentary events*. They can transmit multiple events
+ in a single tick. `EventStream`s do not keep a value set on the last tick on
+ the next tick. They are updated using `EventStream:add`.
+- `IOStream`s are like `EventStream`s, but their `IOStream:tick` method is
+ polled by the event loop at the start of every tick. This gives them a chance
+ to effectively create changes 'out of thin air' and kickstart the execution
+ of the dataflow engine. All *runtime* execution is due to an `IOStream`
+ becoming dirty somewhere.
+
### Op:tick
`Op:tick` is called whenever any of the inputs are *dirty*. This is where the
Op's main logic will go. Generally here it should be checked which input(s)
@@ -120,10 +171,6 @@ Since it is rarely necessary to implement `Action`s, there is currently no
documentation on implementing them, but the `Action` class documentation and the
examples in `core/builtin.moon` should be enough to get started.
-## `IO`s
-
-(wip)
-
[lua]: https://www.lua.org/
[moonscript]: http://moonscript.org/
[builtins-doc]: ../../reference/index.html#doc