Skip to content

Commit

Permalink
make (delete) and (set) work with tuples/vectors/node, change (set) b…
Browse files Browse the repository at this point in the history
…ehaviour to return the entire datastructure (closes #2)
  • Loading branch information
xrstf committed Dec 28, 2023
1 parent c7e8129 commit c44941c
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 249 deletions.
Binary file modified cmd/rudi/docs/data/functions/delete.md.gz
Binary file not shown.
Binary file modified cmd/rudi/docs/data/functions/set.md.gz
Binary file not shown.
38 changes: 18 additions & 20 deletions docs/stdlib/core/delete.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
# delete

`delete` removes a key from an object or an item from a vector (shrinking that
vector by 1 element).

<!-- It must be used with a path expression and can be used
on expressions that support path expressions (e.g. `(delete (do-something).gone)`). -->
vector by 1 element). It must be used on any expression that has a path
expression attached (e.g. `(delete (do-something).gone)`).

Note that `delete` is, like all functions in Rudi, stateless and so does not
modify the given argument directly. To make your changes "stick", use the bang
modifier: `(delete! $var.foo)` – this only makes sense for symbols (i.e.
variables and the global document), which is incidentally also what the bang
modifier itself already enforces. So an expression like `(delete! [1 2][1])` is
invalid.
modify the given argument directly. That is why `delete` can be used on
non-variables/documents, like in `(delete (read-config).password)` to get rid
of individual keys from a larger data structure. To enable this, `delete` always
returns the _remaining_ datastructure, not the value that was removed.

To make your changes "stick", use the bang modifier: `(delete! $var.foo)` – this
only makes sense for symbols (i.e. variables and the global document), since
there is no "source" to be updated for e.g. literals (hence
`(delete! [1 2 3][1])` is invalid).

[`set`](set.md) is another function that is most often used with the bang
modifier.
modifier and also returns the entire datastructure, not just the newly inserted
value.

## Examples

* `(delete! $var.key)`
* `(delete! .[1])`

<!-- This function is mostly used with the bang modifier, but can sometimes also be
This function is mostly used with the bang modifier, but can sometimes also be
useful without if you only want to modify an object/vector "in transit":

* `(handle (delete (read-config).isAdmin))` to give `handle` a config object
object without the `isAdmin` flag. -->
* `(connect (delete (read-config).isAdmin))` to give `connect` a config object
object without the `isAdmin` flag.

## Forms

### `(delete target:symbol)``any`
### `(delete target:pathed)``any`

* `target` is a symbol with a path expression.
* `target` is a any expression with a path expression.

`delete` evaluates the target expression and then removes whatever the path
expression is pointing to. The return value is the remaining data (i.e. not the
removed value).

When used with the bang modifier, `target` must be a symbol with a path
expression.

## Context

`delete` evaluates the expression in its own scope, so variables defined in it
do not leak.
56 changes: 26 additions & 30 deletions docs/stdlib/core/set.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

`set` is used to define variables and to update values of variables or the global
document. Similar to [`delete`](delete.md) it is most often used with the
bang modifier (`(set! …)`), as modifications "do not stick" otherwise.
bang modifier (`(set! …)`), as modifications "do not stick" otherwise. However
`set` can also be used to modify a datastructure "in transit", like in
`(set! .user.settings (set $defaultSettings.isAdmin false))`, where the inner
function will change a subfield (`isAdmin`) but still return the entire
default settings object.

`set` allows both overwriting the entire target value (`(set! $var …)` or
`(set! . …)`) as well as setting just a sub element (for example
Expand All @@ -12,48 +16,40 @@ Variables defined by `set` are scoped and only valid for all following sibling
expressions, never the parent. For for example `(if true (set! $x 42)) $x` is
invalid, as `$x` only exists in the positive branch of that `if` tuple.

`set` returns the value that was set.
`set` returns the entire target data structure, as if no path expression was
given. For example in `(set (read-config).isAdmin false)`, the entire
configuration would be returned, not just `false`. This is slightly different
semantics from most other functions, which would return only the resulting
value (e.g. `(append $foo.list 2)` would not return the entire `$foo` variable,
but only the `list` vector). In that sense, `set` works like `delete`, which
returns the _remaining_ data, not whatever was removed.

## Examples

* `(set! $foo 42)``42`
* `(set! $foo 42) $foo``42`
* `(set! .global[0].document "new-value")``"new-value"`

Without the bang modifier, `set` is less useful:

* `(set! $foo 42) (set $foo "new-value") $foo``42`
* `(set! $config {a "yes" b "yes"}) (set $config.a "no")``{a "yes" b "no"}`

## Forms

### `(set target:symbol value:expression)``any`
### `(set target:pathed value:any)``any`

* `target` is a symbol.
* `target` is any expression that can have a path expression.
* `value` is any expression.

<!-- , but can also be a vector/object/tuple with
path expression, as long as the tuple would evaluate to something where the
path expression fits. -->

`set` evaluates the value and then applies it to the `target`.
`set` evaluates the `value` and then the path expression of `target`. If both
were successfully evaluated, the value is inserted into the target value at
the given path and then the entire target is returned.

If `target` is a variable with no path expression, its value is simply overwritten.
Likewise, a `.` will make `set` overwrite the entire global document.

If a path expression is present, `set` will only set value deeply nested in the
target value (e.g. `(set $foo.bar 42)` will update the value at the key "bar"
in `$foo`, which is hopefully an object). Even in this case, the _return value_
of `set` is still the _set_ value (42 in the previous example), not the combined
value.
Note that for variables, the path expression can be empty (e.g.
`(set $foo 42)`). For all other valid targets, a path expression must be set
(e.g. `(set (read-config).field 42)`) because there is no source that could be
overwritten (like with a variable or the global document).

Also note that without the bang modifier, all of variable and document changes
are only valid inside the `set` tuple itself and will disappear / not leak
outside.

If the `value` or the `target` return an error while evaluating, the error is
returned.

## Context
are only returned, the underlying value is not modified in-place.

`set` evaluates all expressions as their own scopes, so variables from the
`value` expression do not influence the `target` expression's evaluation.
`set!` can only be used with variables and bare path expressions (i.e. the
global document), because there is no logical way to modify the result of a
function call in-place.
38 changes: 18 additions & 20 deletions pkg/builtin/core/docs/delete.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
# delete

`delete` removes a key from an object or an item from a vector (shrinking that
vector by 1 element).

<!-- It must be used with a path expression and can be used
on expressions that support path expressions (e.g. `(delete (do-something).gone)`). -->
vector by 1 element). It must be used on any expression that has a path
expression attached (e.g. `(delete (do-something).gone)`).

Note that `delete` is, like all functions in Rudi, stateless and so does not
modify the given argument directly. To make your changes "stick", use the bang
modifier: `(delete! $var.foo)` – this only makes sense for symbols (i.e.
variables and the global document), which is incidentally also what the bang
modifier itself already enforces. So an expression like `(delete! [1 2][1])` is
invalid.
modify the given argument directly. That is why `delete` can be used on
non-variables/documents, like in `(delete (read-config).password)` to get rid
of individual keys from a larger data structure. To enable this, `delete` always
returns the _remaining_ datastructure, not the value that was removed.

To make your changes "stick", use the bang modifier: `(delete! $var.foo)` – this
only makes sense for symbols (i.e. variables and the global document), since
there is no "source" to be updated for e.g. literals (hence
`(delete! [1 2 3][1])` is invalid).

[`set`](set.md) is another function that is most often used with the bang
modifier.
modifier and also returns the entire datastructure, not just the newly inserted
value.

## Examples

* `(delete! $var.key)`
* `(delete! .[1])`

<!-- This function is mostly used with the bang modifier, but can sometimes also be
This function is mostly used with the bang modifier, but can sometimes also be
useful without if you only want to modify an object/vector "in transit":

* `(handle (delete (read-config).isAdmin))` to give `handle` a config object
object without the `isAdmin` flag. -->
* `(connect (delete (read-config).isAdmin))` to give `connect` a config object
object without the `isAdmin` flag.

## Forms

### `(delete target:symbol)``any`
### `(delete target:pathed)``any`

* `target` is a symbol with a path expression.
* `target` is a any expression with a path expression.

`delete` evaluates the target expression and then removes whatever the path
expression is pointing to. The return value is the remaining data (i.e. not the
removed value).

When used with the bang modifier, `target` must be a symbol with a path
expression.

## Context

`delete` evaluates the expression in its own scope, so variables defined in it
do not leak.
56 changes: 26 additions & 30 deletions pkg/builtin/core/docs/set.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

`set` is used to define variables and to update values of variables or the global
document. Similar to [`delete`](delete.md) it is most often used with the
bang modifier (`(set! …)`), as modifications "do not stick" otherwise.
bang modifier (`(set! …)`), as modifications "do not stick" otherwise. However
`set` can also be used to modify a datastructure "in transit", like in
`(set! .user.settings (set $defaultSettings.isAdmin false))`, where the inner
function will change a subfield (`isAdmin`) but still return the entire
default settings object.

`set` allows both overwriting the entire target value (`(set! $var …)` or
`(set! . …)`) as well as setting just a sub element (for example
Expand All @@ -12,48 +16,40 @@ Variables defined by `set` are scoped and only valid for all following sibling
expressions, never the parent. For for example `(if true (set! $x 42)) $x` is
invalid, as `$x` only exists in the positive branch of that `if` tuple.

`set` returns the value that was set.
`set` returns the entire target data structure, as if no path expression was
given. For example in `(set (read-config).isAdmin false)`, the entire
configuration would be returned, not just `false`. This is slightly different
semantics from most other functions, which would return only the resulting
value (e.g. `(append $foo.list 2)` would not return the entire `$foo` variable,
but only the `list` vector). In that sense, `set` works like `delete`, which
returns the _remaining_ data, not whatever was removed.

## Examples

* `(set! $foo 42)``42`
* `(set! $foo 42) $foo``42`
* `(set! .global[0].document "new-value")``"new-value"`

Without the bang modifier, `set` is less useful:

* `(set! $foo 42) (set $foo "new-value") $foo``42`
* `(set! $config {a "yes" b "yes"}) (set $config.a "no")``{a "yes" b "no"}`

## Forms

### `(set target:symbol value:expression)``any`
### `(set target:pathed value:any)``any`

* `target` is a symbol.
* `target` is any expression that can have a path expression.
* `value` is any expression.

<!-- , but can also be a vector/object/tuple with
path expression, as long as the tuple would evaluate to something where the
path expression fits. -->

`set` evaluates the value and then applies it to the `target`.
`set` evaluates the `value` and then the path expression of `target`. If both
were successfully evaluated, the value is inserted into the target value at
the given path and then the entire target is returned.

If `target` is a variable with no path expression, its value is simply overwritten.
Likewise, a `.` will make `set` overwrite the entire global document.

If a path expression is present, `set` will only set value deeply nested in the
target value (e.g. `(set $foo.bar 42)` will update the value at the key "bar"
in `$foo`, which is hopefully an object). Even in this case, the _return value_
of `set` is still the _set_ value (42 in the previous example), not the combined
value.
Note that for variables, the path expression can be empty (e.g.
`(set $foo 42)`). For all other valid targets, a path expression must be set
(e.g. `(set (read-config).field 42)`) because there is no source that could be
overwritten (like with a variable or the global document).

Also note that without the bang modifier, all of variable and document changes
are only valid inside the `set` tuple itself and will disappear / not leak
outside.

If the `value` or the `target` return an error while evaluating, the error is
returned.

## Context
are only returned, the underlying value is not modified in-place.

`set` evaluates all expressions as their own scopes, so variables from the
`value` expression do not influence the `target` expression's evaluation.
`set!` can only be used with variables and bare path expressions (i.e. the
global document), because there is no logical way to modify the result of a
function call in-place.
Loading

0 comments on commit c44941c

Please sign in to comment.