Super Pipe
H~>
1 Nested Attributes
2 Nested State
8.12

Super Pipe🔗ℹ

Kevin R. Stravers

 (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.

syntax

(H~> table transformation ...)

 
  table : hash?
Uses the state table and applies the transformations to it from left-to-right. Each transformation may introduce new entries in the state, which will be visible for the next transformation. table is an arbitrary expression resulting in hash?.

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.

Simple example of usage
> (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.

Example of all forms:
> (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.

Example of nested attributes:
> (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:

Example of nested substate:
> (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.