diff --git a/exploration/dataflow-composability.md b/exploration/dataflow-composability.md new file mode 100644 index 000000000..e0ea68c15 --- /dev/null +++ b/exploration/dataflow-composability.md @@ -0,0 +1,827 @@ +# Data Flow for Composable Functions + +Status: **Proposed** + +
+ Metadata +
+
Contributors
+
@catamorphism
+
@stasm
+
First proposed
+
2024-02-13
+
Pull Requests
+
#645
+
#646
+
+
+ +## Objective + +Custom formatting functions should be able to +inspect the raw value and formatting options +of their arguments. +In addition, while a custom formatter may eagerly +format its operand to a string, +returning the raw underlying value +and the formatting options used for formatting +are also useful, +in case another function wants to extend these options +or use them for other logic. + +Making the underlying structure of its inputs, +as well as requiring formatters to return structured outputs, +makes it possible to specify how different functions +can be _composed_ together +(as shown in example 1.1 below). + +### Pull Requests + +The pull request for this design document itself is [#645](https://github.com/unicode-org/message-format-wg/pull/645). + +A draft pull request, [#646](https://github.com/unicode-org/message-format-wg/pull/646), +shows what the [formatting spec](https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md) +would look like if this design document were accepted. +(As of this writing, #646 reflects a slightly older version +of this design document, so some of the names used are different.) + +## Background + +In the accepted version of the spec (as of this writing), +the term "resolved value" is used for several different kinds +of intermediate values, +and the structure of resolved values is left completely +implementation-specific. + +Providing a mechanism for custom formatters to inspect more +detailed information about their arguments requires the +different kinds of intermediate values to be differentiated +from each other and more precisely specified. + +At the same time, the implementation can still be given freedom +to define the underlying types for representing formattable values +and formatted results. This proposal just defines wrappers +for those types that implementations must use in order to +make custom functions as flexible as possible. + +## Use-Cases + +Use cases from [issue 515](https://github.com/unicode-org/message-format-wg/issues/515): + +The following code fragment +invokes the `:number` formatter on the literal `1`, +binds the result to `$a`, and then invokes `:number` on the +value bound to `$a`. + +If the value of `$a` does not allow for inspecting the previous options +passed to the first call to `:number`, +then the `$b` would format as `1.000`. + +### Example 1.1 +``` +.local $a = {1 :number minIntegerDigits=3} // formats as 001. +.local $b = {$a :number minFractionDigits=3} // formats as 001.000 +// min integer digits are preserved from the previous call. +``` + +In other words: the user likely expects this code to be equivalent to: + +### Example 1.2 +``` +.local $b = {1 :number minIntegerDigits=3 minFractionDigits=3} +``` + +But without `:number` being able to access the previously passed options, +the two fragments won't be equivalent. +This requires `:number` to return a value that encodes +the options that were passed in, the value that was passed in, +and the formatted result; +not just the formatted result. + +This example is an instance of the basic motivator for this proposal: +allowing data that flows out of a function call +to flow back into another function call +with all of its metadata (e.g. options) preserved. + +### Example 1.3 +``` +.input {$item :noun case=accusative count=1} +.local $colorMatchingGrammaticalNumberGenderCase = {$color :adjective accord=$item} +``` + +The `:adjective` function is a hypothetical custom formatter. +If the value of its `accord` option is a string, it's hard for `:adjective` +to use the value of `accord` to inflect the value of `$color` appropriately +given the value of `$item`. +We want to pass not the formatted result of `{$item :noun case=accusative count=1}` +into `:adjective`, but rather, a structure that encodes that formatted result, +along with the resolved value of `$item` and the names and values of the options +previously passed to `:noun`: `case` and `count`. + +### Example 1.4 + +``` +.local $foo = {$arg :func} +``` + +Here, `$arg` is treated as an input variable. +Suppose that internally, an implementation wants to pre-define +some formatting options on all input variables +(or all input variables whose values have a particular type). +It would be helpful if functions could accept +a single argument that wraps the value of `$arg` +along with these predefined options, +separate from the options that are specific to the function. + +## Requirements + +- Define the structure passed in as an argument to a custom formatting function. +- Define the structure that a custom formatting function should return. +- Maintain the options passed into the callee as a _separate_ argument to the + formatter, to avoid confusion. (See Example 4 below.) +- The structure returned as a value must encode the formatted result, +input value, and options that were passed in. +- Articulate the difference between a "formattable value" +(which is the range of the input mapping (argument mapping), +and the result of evaluating a _literal_) +and a "formatted value" +(which is what a formatting function (usually) returns). +- Clarify the handling of formattable vs. formatted values: +does a formatting function take either, or both? + - This proposal proposes that formatter _inputs_ are a superset of + formatter _outputs_ (in other words, the output of a formatter can + be passed back in to another formatter). + +Any solution should support the examples shown in the "Examples" section. +Minimally, any solution should identify a set of concepts +sufficient for the spec to articulate +that function return values must include a representation of +their input and options, +and not just a "fully formatted" string, other value, or sequence of values. + +## Constraints + +### Implementation-defined behavior + +According to +[the "Introduction" section of the spec](https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#introduction) +> "The form of the resolved value is implementation defined" + +In this proposal, we want to maintain all the +flexibility that implementations require, +as promised by the existing spec, +while also describing more precisely +what an expression "resolves to". + +### Requirements for being formattable + +The same paragraph of the spec describes the form of the resolved value as follows: + +> "...it needs to be "formattable", i.e. it contains everything required by the eventual formatting." + +The purpose of this proposal is to define +what "everything required by the eventual formatting" means, +reconciling the promise of an implementation-defined "resolved value" +with the requirement that implementations preserve +enough metadata when binding names to values +(either directly in a _declaration_ +or indirectly in a function call). + +### Evaluation order + +The spec does not require either eager or lazy evaluation. + +Typically, a lazy implementation has an internal +"thunk" type representing a delayed computation. + +That presents no problems for this proposal, +since we distinguish a _nameable value_ type +(which may appear in the formatter's local environment, +but _not_ as a runtime argument to a function) +that may be distinct from any of the other value types. + +### The function registry + +Function registry contains specifications of the correct "input", +but that's distinct from what we're saying is the "input". +Need the right terminology: +* `:number` takes a string or number (for example), +but that's always wrapped in a (?) `FormattedPlaceholder` thing +that also has fields representing the options from the previous +formatter. + +- See the "Function Resolution" section of the spec + +Step 4: "Call the function implementation with the following arguments..." +* "If the expression includes an operand, its resolved value. + +If the form of the resolved value is implementation-defined, +it's hard to say what the form of the input to the formatting function is, +and likewise its result. + +https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#function-resolution + +### Internal representations + +The interfaces presented in this proposal should +be taken as guidance to implementations +for how to provide the minimum functionality +for composable functions +(how to preserve options through chains of formatters). +The in-memory representation of each type +depends on the programming language +and other implementation choices. +As with the [data model spec](https://github.com/unicode-org/message-format-wg/blob/main/spec/data-model/README.md), +an example model for dataflow is presented here +using TypeScript notation. + +## Proposed Design + +### Taxonomy + + +This proposal introduces several new concepts +and eliminates the term "resolved value" from the spec. + +* _Nameable value_: A value that a variable can be bound to +in the runtime environment that defines local variables. +This concept is introduced to make it easier for the spec +to formally account for both eager and lazy evaluation. +In an eager implementation, "nameable value" is synonymous +with "annotated formattable value", +while in a lazy evaluation, a "nameable value" would be a +closure (code paired with in-scope set of variables). + +> [!IMPORTANT] +> +> In the rest of this proposal, we elide the distinction +> between a _nameable value_ and +> an _annotated formattable value_, +> as the semantics of MessageFormat can be +> implemented either eagerly or lazily +> with the same observable results +> (other than possible differences in the set of errors). +> Examples assume eager evaluation. +> In a lazy implementation, _nameable values_ would +> be constructed when processing a _declaration_, +> and these values would be _forced_ when +> formatting a _pattern_ requires it. + +* _Formattable value_: An implementation-specific type +that is the range of the input mapping (message arguments), +as well as what literals format to. + +* _Formatted value_: The result of a formatting function. +This type is implementation-specific, but expected to +at least include strings. + +* _Formatted part_: The ultimate result of formatting +a part of a pattern (_text_ or _expression_). +In an implementation that offers "formatting to parts" +(as in [the Formatted Parts proposal](./formatted-parts.md)), +the _formatted part_ type might be the same as the +_formatted value_ type, or it might be different. +(In the latter case, the implementation might apply an +implementation-specific transformation +that maps a _formatted value_ onto a _formatted part_ +in a particular _formatting context_.) + +* _Fallback value_: A value representing a formatting error. + * For simplicity, this proposal elides the details of + error handling and thus this type is + not discussed further. + +* _Annotated formattable value_: Encapsulates any +value that can be passed as an argument to a formatting function, +including: + * formattable values that don't yet have formatted output + associated with them + * previously formatted values, which can be reformatted by + another formatting function + * fallback values +The values of named options passed to formatting functions +are also _annotated formattable values_. + +> NOTE: Names are subject to change; an _annotated formattable value_ +> could also be called an _operand value_, +> since it has the capability of being passed into a function. + +* _Markup value_: The result of formatting a markup item +(disjoint from _annotated formattable values_). + * For simplicity, we elide the details of markup values + from this proposal. + +* _Preformatted value_: A formattable value paired with a +formatted value (and some extra information). + +> NOTE: A _preformatted value_ could also be called an _annotated value_ +> (see Example 1.4) + +In the current spec, a "resolved value" can be a +nameable value, formattable value, or preformatted value, +depending on context. + +This proposal keeps the "formattable value" and "formatted value" +types implementation-specific while defining wrapper types +around them, which is intended to strike a balance between +freedom of choice for implementors +and specifiability. + +The following diagram shows the relationships between types, +for a typical implementation that supports formatting to parts. +The largest red box shows the boundaries of the formatter itself. +_Formattable values_ flow into the formatter from the input mapping +and from source code (literals). +Squiggly green lines indicate inclusion (for example, +an _annotated formattable value_ includes a _formattable value_, +as shown by the green line +from `AnnotatedFormattableValue` to `FormattableValue`.) +The orange box represents the execution context for functions. +_Annotated formattable values_ flow in and out of it +through calls and returns (the call and return operations +are implementation-specific.) +An implementation-specific `format` operation can +map a _formattable value_ to a _formatted value_ +(when a value needs to be formatted with defaults according to its type) +as well as mapping a _formatted value_ to a _formatted part_. + +![Diagram of different value types](./dataflow1.jpg) + +#### Example Implementation + +The following diagram is a schematic of +an _example_ implementation. +The example is an eager implementation +that provides "format to parts" functionality. +(For expository reasons, we define a separate +_nameable value_ type even though this would +be the same as _annotated formattable value_.) + +The schematic does not show +inclusions between option lists +and other types. + +![Diagram of example implementation](./dataflow2.jpg) + +A way to think about an implementation declaratively +is to specify some operations and their type signatures. + +Types: + +- Environment : Name → NameableValue +- Callable : Name × OptionMap +- OptionMap : Name → AnnotatedFormattableValue + +Operations: + +* FORCE : NameableValue → AnnotatedFormattableValue +* STORE : Environment -> Name -> NameableValue -> Environment +* LOOKUP-GLOBAL : Name -> FormattableValue +* LOOKUP-LOCAL : Environment -> Name -> NameableValue +* EVAL-LITERAL : Literal -> String +* STRING-TO-FORMATTABLE : String -> FormattableValue +* CALL : Callable -> AnnotatedFormattableValue -> AnnotatedFormattableValue +* FORMAT-TO-VALUE : FormattableValue -> FormattedValue +* FORMAT-TO-PART : FormattedValue -> FormattedPart + +(This presentation does not handle errors, e.g., +what happens if LOOKUP-GLOBAL is invoked on a name +it doesn't have a mapping for.) + +In the diagram, single green arrows represent extracting a named field, +and double green arrows represent wrapping an object in a larger object. +Pink arrows represent operations listed above. + +In this model, summarizing the relationships between the types: + +* A _nameable value_ can be FORCED to an _annotated formattable value_. +* A _formattable value_ can be wrapped in an _annotated formattable value_ + and can be extracted from an _annotated formattable value_. + It can also be queried from the input mapping via LOOKUP-GLOBAL + and can be formatted from a literal via EVAL-LITERAL. +* An _annotated formattable value_ can be STORED in a local environment. + It can be queried from a local environment via LOOKUP-LOCAL. + It can be passed to a function via CALL. + It can be returned from a function via CALL. +- A _preformatted value_ can be extracted from an _annotated formattable value_. +* A _formatted value_ can be obtained from an arbitrary string, + or formatted from a _formattable value_ via FORMAT-TO-VALUE, + or extracted from a _preformatted value_. + It can be formatted to a _formatted part_ via FORMAT-TO-PART. +* A _formatted part_ can be formatted from a _formatted value_ via FORMAT-TO-PART. + +The above exploration is not normative; +rather, it shows how these types could be used to structure an implementation. + +### Interfaces + +Since the rest of the proposal assumes eager evaluation, +this omits a definition for _nameable values_. + +``` +interface FormattableValue = { + type: "formattable"; + value: /* implementation-dependent */; +} + +interface FormattedValue = { + type: "formatted"; + value: /* implementation-dependent */; +} + +interface FormattedPart = { + type: "formattedPart"; + value: /* implementation-dependent */; +} + +type FormatterInput = + | FallbackValue + | FormattableValue + | PreformattedValue; + +interface AnnotatedFormattableValue = { + type: "annotatedFormattable"; + source: string; + value: FormatterInput; +} + +interface FallbackValue = { + type: "fallback"; + fallback: string; +} + +interface PreformattedValue = { + type: "preformatted" + options: Iterable<{ name: string; value: AnnotatedFormattableValue}>; + formatter: string; + input: AnnotatedFormattableValue; + output?: FormattedValue; +} +``` + +Implementations are free to extend the `AnnotatedFormattableValue` +and `PreformattedValue` interfaces with additional fields. + +For example, an implementation could add a `source-text` field +to the `AnnotatedFormattableValue` interface +to track the text (concrete syntax) +of a MessageFormat expression +that was used to construct the value, +for error diagnostics. + +### Function Signatures + +In C++, for example, the interface for custom formatters could look like: + +``` +virtual AnnotatedFormattableValue customFormatter(AnnotatedFormattableValue&& argument, + std::map&& options) const = 0; +``` + +Note that this proposal is orthogonal to the existing +[data model and validation rules for the function registry](https://github.com/unicode-org/message-format-wg/blob/main/spec/registry.md). +Input validation can still be applied to the underlying _formattable values_ and _formatted values_ +wrapped in the `argument` and the values for the `options`. + +## Examples + +### Example 2.1 + +Same code as Example 1.1: +``` +.local $a = {1 :number minIntegerDigits=3} // formats as 001. +.local $b = {$a :number minFractionDigits=3} // formats as 001.000 +``` + +In an implementation with a `FormattedValue` type +that includes a `FormattedNumber` variant, +the right-hand side of `$a` is evaluated to the following _annotated formattable value_: + +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'minIntegerDigits'; value: AnnotatedFormattableValue { + value: FormattableValue ('3')}}]; + formatter: "number", + input: AnnotatedFormattableValue { + value: FormattableValue('1')}; + output: FormattedValue { value: FormattedNumber('001.') }}} +``` + +Then, the right-hand side of `$b` is evaluated +in an environment that binds the name `a` +to the above _annotated formattable value_. +The result is: +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'minFractionDigits'; value: AnnotatedFormattableValue { + value: FormattableValue('3')}}]; + formatter: "number"; + input: AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'minIntegerDigits'; value: AnnotatedFormattableValue { + value: FormattableValue('3')}}]; + formatter: "number", + input: AnnotatedFormattableValue { value: FormattableValue('1')} + output: FormattedValue { value: FormattedNumber('001.') }}}; + output: FormattedValue { value: FormattedNumber('001.000') } }} +``` + +Notice that in the second object, +the `input` field's contents are identical to the first object. + +When calling the implementation of the built-in `number` +formatting function (call it `Number::format()`), +while evaluating the right-hand side of `$b`, +`number` receives two sets of options, in different ways: +* The option `minFractionDigits=3` is passed to `Number::format()` + in its `options` argument. +* The option `minIntegerDigits=3` is embedded in the `argument` value. + +In general, the previous formatter might be different from +`Number::format()`, so all the "previous" options have to be +separated from the options in the option map. +The solution in this proposal encodes them in a tree-like structure +representing the chain of previous formatting calls +(really a list-like structure, since formatters have a single argument). + +### Example 2.2 + +This example motivates why option values need to be +_annotated formattable values_. + +Same code as Example 1.3: +``` +.input {$item :noun case=accusative count=1} +.local $colorMatchingGrammaticalNumberGenderCase = {$color :adjective accord=$item} +``` + +When processing the `.input` declaration, +supposing that `item` is bound in the input mapping +to the string 'balloon', +and `color` is bound in the input mapping +to the string 'red', +the name `item` is bound in the runtime environment +to the following _annotated formattable value_: + + +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'case'; value: AnnotatedFormattableValue { + value: FormattableValue('accusative')}}, + { name: 'count'; value: AnnotatedFormattableValue { + value: FormattableValue('1')}}]; + formatter: "noun", + input: AnnotatedFormattableValue { + value: FormattableValue('balloon')}; + output: FormattedValue { value: 'balloon' }}} +``` + +(As this example uses English, the `case` option has no effect +on the formatted output.) + +Then, when processing the right-hand side of the `local` declaration, +the argument to the `adjective` formatter is as follows: + +``` +AnnotatedFormattableValue { + value: FormattableValue('red') +} +``` + +and the option mapping maps the name 'accord' to the same +_annotated formattable value_ that was returned by the `noun` formatter, +shown above. + +The result of the call to the `adjective` formatter looks like: + +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: [{name: 'accord'; value: AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'case'; value: AnnotatedFormattableValue { + value: FormattableValue('accusative')}}, + { name: 'count'; value: AnnotatedFormattableValue { + value: FormattableValue('1')}}]; + formatter: "noun", + input: AnnotatedFormattableValue { + value: FormattableValue('balloon')}; + output: FormattedValue { value: 'balloon' }}}}]; + formatter: 'adjective'; + input: AnnotatedFormattableValue { value: FormattableValue('red') }; + output: FormattedValue { value: 'red' }}} +``` + +Note that the value of the 'accord' option +in the outer `options` field of the `value` field +is the same as the first `AnnotatedFormattableValue` in this subsection. +As before, since the example uses English, the `accord` option has +no effect on the output. + +If the output of the `adjective` formatter was formatted by a subsequent +formatter, it would be able to inspect the value of the output's +`accord` option, along with all of **its** options. + +### Example 2.3 + +For the same code as Example 1.4: +``` +.local $foo = {$arg :func} +``` + +To get the custom functionality described in that example, +the formatter would proceed as follows, +supposing that in the _input mapping_, the name `arg` +is bound to the value `42`, +and the implementation chooses to annotate all integer inputs +with the option `foo`. + +* Bind the name `foo` to the following _annotated formattable value_. +* Pass an argument with the following structure to `func`: + +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'foo'; value: FormattableValue('bar') } /* , etc. */]; + formatter: "default", + input: AnnotatedFormattableValue { + value: FormattableValue('42')]; + } +} +``` + +("Default" is just an arbitrary name for the purposes of the example, +The implementation could choose any unbound formatter name +to indicate that +this value was constructed by applying "default options" +rather than calling a formatter.) + +If the function `func` returns the string +`"baz"` in this case, +the return value from `func` would look like: + +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: []; + formatter: "func"; + input: AnnotatedFormattableValue { + value: PreformattedValue { + options: [{ name: 'foo'; value: FormattableValue('bar') } /* , etc. */]; + formatter: "default", + input: AnnotatedFormattableValue { + value: FormattableValue('42')]; + }} + output: FormattedValue('baz'); + } +} +``` + +Note that the outer `input` field is the same as +the previous `AnnotatedFormattableValue`. + +## Alternatives Considered + +### Severely limit how local variables are used + +Perhaps the most restrictive option is to forbid composition by +restricting where local variables can be referenced. + +Suppose that local variables can only be used in patterns, +not in the right-hand sides of subsequent declarations. +Furthermore, suppose that they can only appear unannotated +in patterns. This could be enforced via a new type of data model +error. + +This is probably too severe, because we want to be able to write: + +``` +.local $x = {1} +.match {$x :number} +* {{wildcard}} +``` + +and not just: + +``` +.match {1 :number} +* {{wildcard}} +``` + +Such a solution would make local declarations much less useful. + +### Not defining the shape of inputs or outputs to custom formatters + +Leave it to implementations, +as is currently done in the spec with "resolved values". + +The disadvantage of this approach is that +it means an implementation can be spec-compliant +without providing composable functions. + +### Functions return minimal results; formatter fills in extra results + +In this alternative: the function argument would still be +an _annotated formattable value_, +but the function can just return a _formatted value_ +since otherwise, it's just copying identical fields into the +result (the source text doesn't change; the formatter name +and resolved options are already known by the caller code +in the MesageFormat implementation; etc.) + +But, what happens if a function "wants" to just +return the _formattable value_ that is passed in; +if the result type of the function is the same as _formatted value_, +then this can't be expressed. This suggests: + +### Functions return the union of _formatted value_ and a _formattable value_ + +This is hard to express in some programming languages' type systems. + +### Encode metadata in formatting context + +The argument to the function would be the union of +a _formattable value_ and a _formatted value_ (to allow reformatting) +and the function would have to access the formatting context to +query its previously-passed options, etc. + +This does make the common case simple (most custom functions will +probably not need to inspect values in this way), +but allowing the argument to be a union type +also makes it hard to express the function signature +in some programming languages. + +### Functions don't preserve options and can't inspect previous options + +May violate intuition (as with the number example) +or make grammatical transformations much harder to implement +(as in the accord example) + +### Other representations + +* Alternative: No _annotated formattable values_; just +a _formattable value_ with some required fields, and +specify that the implementation may add further fields. + +* Alternative: Still have an _annotated formattable value_ +that wraps a _formattable value_, +but instead of having a separate _preformatted value_ type, +combine the two types and consider some fields optional +(i.e. the fields that only appear in a _preformatted value_ +and not a _formattable value_). + +* Alternative: flat structure instead of tree structure. Consider: + +Consider: + +``` +AnnotatedFormattableValue { + value: PreformattedValue { + options: [{'a': AnnotatedFormattableValue(FormattableValue(1))}]; + formatter: "F", + input: AnnotatedFormattableValue { + value: PreformattedValue { + options: [{'b', AnnotatedFormattableValue(FormattableValue(2))}]; + formatter: "F"; + input: AnnotatedFormattableValue { + value: PreformattedValue { + options: [{'c', AnnotatedFormattableValue(FormattableValue(3))}]; + formatter: "F"; + input: AnnotatedFormattableValue(FormattableValue('foo')); + output: FormattedValue("X"); + } + } + output: FormattedValue("Y")}} + output: FormattedValue("Z")}} +``` + +for some formatter F. Recursing through this tree structure to find all the previous options +might be tedious. +For nested values where the formatter is the same for all the nested values, +a single `options` list might be more convenient. +However, it's unclear how to use a flat representation if the nested values +are produced by different formatters that take different options. +What if the output of formatter `F`, which was passed an option `a`, +is passed back into another formatter `G` which also takes an option `a`, +that has a different semantics from `F`'s semantics for `a`? + +### Restricting composition + +If composition of different functions was disallowed +(made into a data model error), then +there would be no need for a common representation of output values +for all functions. + +## Incidental notes + +The spec currently says: + +> Function access to the _formatting context_ MUST be minimal and read-only, +> and execution time SHOULD be limited. + +Choosing the right representation for _annotated formattable values_ +might reduce the need for functions to access the _formatting context_, +though it would probably not eliminate that need. diff --git a/exploration/dataflow1.jpg b/exploration/dataflow1.jpg new file mode 100644 index 000000000..53583149c Binary files /dev/null and b/exploration/dataflow1.jpg differ diff --git a/exploration/dataflow2.jpg b/exploration/dataflow2.jpg new file mode 100644 index 000000000..dd8bd451c Binary files /dev/null and b/exploration/dataflow2.jpg differ