Super Pipe
(require spipe) | package: spipe |
The H~> form implements hash-based pipeline programming. H~> functionally threads state (a hash-table) through provided transformations. Each transformation specifies which elements to access and update in the hash table.
The grammar of transformtion follows, where non-italicized identifiers are recognized symbolically.
transformation | = | callee-id | ||
| | (callee) | |||
| | (callee *) | |||
| | (callee read-writes ...+) | |||
| | (callee (reads ...) (writes ...)) | |||
| | (callee (reads ...)) |
The first transformation form callee-id is always transformed into (callee).
(callee) applies the form with the entire state as its argument.
(callee *) is a special form that is similar to (callee) but ignores the return value.
(callee read-writes ...+) simultaneously specifies variables to read from and write to. The reads are given as arguments to callee in the order they are specified. The writes are assigned to the returned values in the order they are specified.
(callee (reads ...) (writes ...)) specifies reads and writes separately. Left-to-right order applies.
(callee (reads ...)) for when you intend to not write to any entry.
> (H~> (hash 'hello "to you " 'world 2) (number->string world) (string-append (hello world) (hw))) '#hash((hello . "to you ") (world . "2") (hw . "to you 2"))
Note that the above adds the symbol 'hw to the hash-table. If a write location does not exist, it is created. Entries in the hash table can not be deleted inside H~> without using external functions.
> (H~> (hash 'hello "hi" 'world "u") (write *) (print *) (identity) ((const "you") world) (string-append (hello world) (hello-world)) (displayln (hello-world))) #hash((hello . "hi") (world . "u"))'#hash((hello . "hi") (world . "u"))hiyou
'#hash((hello-world . "hiyou") (hello . "hi") (world . "you"))
1 Nested Attributes
Sometimes it’s useful to access nested hash tables. H~> provides functionality for this by parsing any identifier containing dots. Each dot represents a sub-table entry.
> (H~> (hash) ((const 'value) () (a.b.c.target)) (write (a.b))) #hasheq((c . #hasheq((target . value))))
'#hash((a . #hasheq((b . #hasheq((c . #hasheq((target . value))))))))
For writes, if a subtable does not exist, it is created. However, if such creation would overwrite already-existing values, an error is thrown. For reads, non-existing subtables return #f.
2 Nested State
It may not always be ergonomic to refer to the full path of the state, so to allow nesting of substate access we can use H~> inside itself:
> (H~> (hash) ((const 'value) () (a.b.c.target)) (H~> a.b (symbol->string c.target))) '#hash((a . #hasheq((b . #hasheq((c . #hasheq((target . "value"))))))))
This is a special form and does not follow the conventional (F (in) (out)) rules.