Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add :root and :in selectors, fix variable bug #1690

Merged
merged 1 commit into from
Mar 23, 2023
Merged

Conversation

mtdowling
Copy link
Member

This commit adds support for the :in and :root functions. The :in function is a simpler way to check if a shape is in an expression, typically used to test if a variable contains a shape or if a root expression contains a shape. :root is used to create rooted common subexpressions that are evaluated once against every shape in the model. :root expressions are evaluate in an isolated context, so any variables used or stored by them are not accessible outside the root selector. :root selectors allows selection to be broken into multiple steps and evaluate globally.

Let's say you want all number shapes that are used in operation inputs, but not used in operation outputs. This can be done today using the following expression:

service
$outputs(~> operation -[output]-> ~> number)
~> operation -[input]-> ~> number
:not([@: @{id} = @{var|outputs|id}])

With the addition of the ``:in` selector, this gets easier because we can avoid using a scoped attribute selector:

service
$outputs(~> operation -[output]-> ~> number)
~> operation -[input]-> ~> number
:not(:in(${outputs}))

(Note: to make this work, I had to uncover and fix a bug in the implementation of how we store variables. We previously used Collection#add as a Receiver, but that method will return false if it's already seen a shape, which is wrong.)

With the addition of :root, you can use a much simpler expression:

number
:in(:root(service ~> operation -[input]-> ~> number))
:not(:in(:root(service ~> operation -[output]-> ~> number)))

(Note: the result of root expressions are run once and cached. No need to store them in a variable)

These expressions seem to be exactly the same, however, the :root expression gives a different result when working with models that contain multiple services. In the first two expressions, if any service uses shape X in input and not output, then X is a result. However, in the :root expression, X is only part of the result if no service uses it in their output shape closures.

Issue #, if available:

Description of changes:

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@mtdowling mtdowling requested a review from a team as a code owner March 23, 2023 06:36
This commit adds support for the `:in` and `:root` functions. The
`:in` function is a simpler way to check if a shape is in an expression,
typically used to test if a variable contains a shape or if a root
expression contains a shape. `:root` is used to create rooted common
subexpressions that are evaluated once against every shape in the
model. `:root` expressions are evaluate in an isolated context, so
any variables used or stored by them are not accessible outside the
root selector. `:root` selectors allows selection to be broken into
multiple steps and evaluate globally.

Let's say you want all number shapes that are used in operation
inputs, but not used in operation outputs. This can be done today
using the following expression:

```
service
$outputs(~> operation -[output]-> ~> number)
~> operation -[input]-> ~> number
:not([@: @{id} = @{var|outputs|id}])
```

With the addition of the ``:in` selector, this gets easier because we
can avoid using a scoped attribute selector:

```
service
$outputs(~> operation -[output]-> ~> number)
~> operation -[input]-> ~> number
:not(:in(${outputs}))
```

(Note: to make this work, I had to uncover and fix a bug in the
implementation of how we store variables. We previously used
`Collection#add` as a `Receiver`, but that method will return false if
it's already seen a shape, which is wrong.)

With the addition of `:root`, you can use a much simpler expression:

```
number
:in(:root(service ~> operation -[input]-> ~> number))
:not(:in(:root(service ~> operation -[output]-> ~> number)))
```

(Note: the result of root expressions are run once and cached. No
need to store them in a variable)

These expressions _seem_ to be exactly the same, however, the `:root`
expression gives a different result when working with models that
contain multiple services. In the first two expressions, if any
service uses shape X in input and not output, then X is a result.
However, in the `:root` expression, X is only part of the result if
no service uses it in their output shape closures.
@mtdowling mtdowling force-pushed the add-subexpressions branch from 2b4f78e to 5a21517 Compare March 23, 2023 18:13
@mtdowling mtdowling merged commit edd987a into main Mar 23, 2023
@mtdowling mtdowling deleted the add-subexpressions branch March 24, 2023 05:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants