Module dev_stack.erl¶
A device that contains a stack of other devices, and manages their execution.
Description¶
It can run in two modes: fold (the default), and map.
In fold mode, it runs upon input messages in the order of their keys. A stack maintains and passes forward a state (expressed as a message) as it progresses through devices.
For example, a stack of devices as follows:
When called with the message:
Will produce the output:
In map mode, the stack will run over all the devices in the stack, and
combine their results into a single message. Each of the devices'
output values have a key that is the device's name in the Device-Stack
(its number if the stack is a list).
You can switch between fold and map modes by setting the Mode
key in the
Msg2
to either Fold
or Map
, or set it globally for the stack by
setting the Mode
key in the Msg1
message. The key in Msg2
takes
precedence over the key in Msg1
.
The key that is called upon the device stack is the same key that is used upon the devices that are contained within it. For example, in the above scenario we resolve FuncName on the stack, leading FuncName to be called on Add-One-Device and Add-Two-Device.
A device stack responds to special statuses upon responses as follows:
skip
: Skips the rest of the device stack for the current pass.
pass
: Causes the stack to increment its pass number and re-execute
the stack from the first device, maintaining the state
accumulated so far. Only available in fold mode.
In all cases, the device stack will return the accumulated state to the caller as the result of the call to the stack.
The dev_stack adds additional metadata to the message in order to track the state of its execution as it progresses through devices. These keys are as follows:
Stack-Pass
: The number of times the stack has reset and re-executed
from the first device for the current message.
Input-Prefix
: The prefix that the device should use for its outputs
and inputs.
Output-Prefix
: The device that was previously executed.
All counters used by the stack are initialized to 1.
Additionally, as implemented in HyperBEAM, the device stack will honor a number of options that are passed to it as keys in the message. Each of these options is also passed through to the devices contained within the stack during execution. These options include:
Error-Strategy
: Determines how the stack handles errors from devices.
See maybe_error/5
for more information.
Allow-Multipass
: Determines whether the stack is allowed to automatically
re-execute from the first device when the pass
tag is returned. See
maybe_pass/3
for more information.
Under-the-hood, dev_stack uses a default
handler to resolve all calls to
devices, aside set/2
which it calls itself to mutate the message's device
key in order to change which device is currently being executed. This method
allows dev_stack to ensure that the message's HashPath is always correct,
even as it delegates calls to other devices. An example flow for a dev_stack
execution is as follows:
/Msg1/AlicesExcitingKey ->
dev_stack:execute ->
/Msg1/Set?device=/Device-Stack/1 ->
/Msg2/AlicesExcitingKey ->
/Msg3/Set?device=/Device-Stack/2 ->
/Msg4/AlicesExcitingKey
... ->
/MsgN/Set?device=[This-Device] ->
returns {ok, /MsgN+1} ->
/MsgN+1
In this example, the device
key is mutated a number of times, but the
resulting HashPath remains correct and verifiable.
Function Index¶
benchmark_test/0* | |
example_device_for_stack_test/0* | |
generate_append_device/1 | |
generate_append_device/2* | |
increment_pass/2* | Helper to increment the pass number. |
info/1 | |
input_and_output_prefixes_test/0* | |
input_output_prefixes_passthrough_test/0* | |
input_prefix/3 | Return the input prefix for the stack. |
many_devices_test/0* | |
maybe_error/5* | |
no_prefix_test/0* | |
not_found_test/0* | |
output_prefix/3 | Return the output prefix for the stack. |
output_prefix_test/0* | |
pass_test/0* | |
prefix/3 | Return the default prefix for the stack. |
reinvocation_test/0* | |
resolve_fold/3* | The main device stack execution engine. |
resolve_fold/4* | |
resolve_map/3* | Map over the devices in the stack, accumulating the output in a single message of keys and values, where keys are the same as the keys in the original message (typically a number). |
router/3* | |
router/4 | The device stack key router. |
simple_map_test/0* | |
simple_stack_execute_test/0* | |
skip_test/0* | |
test_prefix_msg/0* | |
transform/3* | Return Message1, transformed such that the device named Key from the
Device-Stack key in the message takes the place of the original Device
key. |
transform_external_call_device_test/0* | Ensure we can generate a transformer message that can be called to return a version of msg1 with only that device attached. |
transform_internal_call_device_test/0* | Test that the transform function can be called correctly internally by other functions in the module. |
transformer_message/2* | Return a message which, when given a key, will transform the message
such that the device named Key from the Device-Stack key in the message
takes the place of the original Device key. |
Function Details¶
benchmark_test/0 *¶
benchmark_test() -> any()
example_device_for_stack_test/0 *¶
example_device_for_stack_test() -> any()
generate_append_device/1¶
generate_append_device(Separator) -> any()
generate_append_device/2 *¶
generate_append_device(Separator, Status) -> any()
increment_pass/2 *¶
increment_pass(Message, Opts) -> any()
Helper to increment the pass number.
info/1¶
info(Msg) -> any()
input_and_output_prefixes_test/0 *¶
input_and_output_prefixes_test() -> any()
input_output_prefixes_passthrough_test/0 *¶
input_output_prefixes_passthrough_test() -> any()
input_prefix/3¶
input_prefix(Msg1, Msg2, Opts) -> any()
Return the input prefix for the stack.
many_devices_test/0 *¶
many_devices_test() -> any()
maybe_error/5 *¶
maybe_error(Message1, Message2, DevNum, Info, Opts) -> any()
no_prefix_test/0 *¶
no_prefix_test() -> any()
not_found_test/0 *¶
not_found_test() -> any()
output_prefix/3¶
output_prefix(Msg1, Msg2, Opts) -> any()
Return the output prefix for the stack.
output_prefix_test/0 *¶
output_prefix_test() -> any()
pass_test/0 *¶
pass_test() -> any()
prefix/3¶
prefix(Msg1, Msg2, Opts) -> any()
Return the default prefix for the stack.
reinvocation_test/0 *¶
reinvocation_test() -> any()
resolve_fold/3 *¶
resolve_fold(Message1, Message2, Opts) -> any()
The main device stack execution engine. See the moduledoc for more information.
resolve_fold/4 *¶
resolve_fold(Message1, Message2, DevNum, Opts) -> any()
resolve_map/3 *¶
resolve_map(Message1, Message2, Opts) -> any()
Map over the devices in the stack, accumulating the output in a single message of keys and values, where keys are the same as the keys in the original message (typically a number).
router/3 *¶
router(Message1, Message2, Opts) -> any()
router/4¶
router(Key, Message1, Message2, Opts) -> any()
The device stack key router. Sends the request to resolve_stack
,
except for set/2
which is handled by the default implementation in
dev_message
.
simple_map_test/0 *¶
simple_map_test() -> any()
simple_stack_execute_test/0 *¶
simple_stack_execute_test() -> any()
skip_test/0 *¶
skip_test() -> any()
test_prefix_msg/0 *¶
test_prefix_msg() -> any()
transform/3 *¶
transform(Msg1, Key, Opts) -> any()
Return Message1, transformed such that the device named Key
from the
Device-Stack
key in the message takes the place of the original Device
key. This transformation allows dev_stack to correctly track the HashPath
of the message as it delegates execution to devices contained within it.
transform_external_call_device_test/0 *¶
transform_external_call_device_test() -> any()
Ensure we can generate a transformer message that can be called to return a version of msg1 with only that device attached.
transform_internal_call_device_test/0 *¶
transform_internal_call_device_test() -> any()
Test that the transform function can be called correctly internally by other functions in the module.
transformer_message/2 *¶
transformer_message(Msg1, Opts) -> any()
Return a message which, when given a key, will transform the message
such that the device named Key
from the Device-Stack
key in the message
takes the place of the original Device
key. This allows users to call
a single device from the stack:
/Msg1/Transform/DeviceName/keyInDevice -> keyInDevice executed on DeviceName against Msg1.