๐Ÿ 

ZefOps patterns

match, rearrange, the Z placeholder, and the other clever ones.

Z โ€” the anonymous argument

Z is the "it" in a pipeline. Think of it as shorthand for "whatever comes in from the pipe":

5 | apply(Z + 1) | collect               # 6
5 | apply(Z * Z) | collect               # 25
5 | apply(Z > 3) | collect               # True
[1, 2] | apply(Z[0] + Z[1]) | collect  # 3

Under the hood, Z + 1 isn't just "do a sum." It's a symbolic expression โ€” a tiny AST you can pass around as a value.

Z is a placeholder, not a variable

If you wrote lambda x: x + 1, Python builds a closure object with bytecode. If you write Z + 1, Zef builds a symbolic value that says "add one to whatever comes in." That value can be introspected, sent over the network, stored in a database. Lambdas can't.

F โ€” field projector

F is Z's cousin, specialized for "pick this field":

data = {'name': 'Alice', 'age': 30}
data | F.name | collect              # 'Alice'

# chain for deep paths
doc | F.user | F.profile | F.bio | collect

And it composes with Z-style operators:

people | filter(F.age > 18) | collect
people | sort_by(F.age) | collect
people | group_by(F.dept) | collect

match โ€” pattern dispatch

Python's if/elif/else with types as first-class patterns:

142 | match([
    ({42, -42}, 'special'),     # set = "matches any member"
    (Int,            to_string),     # type = "matches instances"
    (String,         identity),      # ditto
    (Any,            'fallback'),
]) | collect
# โ†’ '142'

Patterns are checked top-to-bottom. First match wins.

pattern types

patternmatches when
42value equals 42
{1, 2, 3}value is in the set
Intvalue is_a Int
Int & (Z > 0)refinement type
Anycatch-all

worked example: the type tagger

items = [1, 2.5, 'hello', [1, 2], True]

items | map(match([
    (Int,    Z | apply(lambda x: f"int: {x}")),
    (Float,  Z | apply(lambda x: f"float: {x}")),
    (String, Z | apply(lambda x: f"str: {x}")),
    (Array,  constant_func('list')),
    (Bool,   constant_func('bool')),
    (Any,    constant_func('other')),
])) | collect

order matters!

Put narrower patterns first. Bool is a subtype of Int in Python โ€” if you match Int first, True becomes "int: True". Swap the order or use an explicit isinstance(..., bool)-style guard.

rearrange โ€” templates with z

Reshape data using a template that looks like the target output. Use lowercase z (a template placeholder) to extract from the input:

['Alice', 30, 'Berlin'] | rearrange({
    'name': z[0],
    'age':  z[1] + 10,
    'city': z[2] | to_upper_case,
}) | collect
# {'name': 'Alice', 'age': 40, 'city': 'BERLIN'}
mental model โ€” rearrange

Think of rearrange as "the template is the shape of the answer." Zef walks the template, replaces every z[i] with the i-th input, evaluates expressions, and returns the reshaped value.

it works with any container shape

# dict template โ†’ dict
[1, 2] | rearrange({'a': z[0], 'b': z[1]}) | collect

# list template โ†’ list
[1, 2, 3] | rearrange([z[2], z[0], z[1]]) | collect

# entity template โ†’ entity
['Alice', 30] | rearrange(
    ET.Person(name=z[0], age=z[1] + 10)
) | collect

nested templates

['Alice', 30, 'Berlin', 'Engineer'] | rearrange({
    'personal': {'name': z[0], 'age': z[1]},
    'professional': {'city': z[2], 'role': z[3]},
}) | collect

apply_functions โ€” zip fns with elements

[10, 11, 'hello'] | apply_functions([
    add(1),
    multiply(2),
    to_upper_case,
]) | collect
# [11, 22, 'HELLO']

The nth function applies to the nth element. Lengths must match.

apply_dict โ€” use a dict as a lookup function

'b' | apply_dict({'a':1, 'b':2, 'c':3}) | collect      # 2

# with a default:
'z' | apply_dict({'a':1}, 'not found') | collect     # 'not found'

# combine with map for batch lookups:
['US', 'DE', 'JP'] | map(apply_dict({
    'US': 'United States',
    'DE': 'Germany',
    'JP': 'Japan',
})) | collect

sliding + window_reduce โ€” moving windows

[1, 2, 3, 4, 5] | sliding(3) | collect
# [[1,2,3], [2,3,4], [3,4,5]]

# moving sum
[1, 2, 3, 4, 5] | window_reduce(add, 3) | collect
# [6, 9, 12]

# moving average helper
avg3 = window_reduce(add, 3) | map(Z / 3)
[10, 20, 30, 40] | avg3 | collect
# [20.0, 30.0]

log โ€” the debugger you deserve

data | op1 | log | op2 | log | op3 | collect
# prints:
# ๐Ÿชต: <value after op1>
# ๐Ÿชต: <value after op2>

log is a pass-through โ€” it prints the value and hands it to the next stage unchanged.

named logs

data | op1 | log('step 1') | op2 | log('step 2') | collect

a fully worked example

Suppose you have a list of log lines. You want to:

  1. Parse each one as JSON
  2. Keep only ERROR-level entries
  3. Group by service name
  4. For each service, count and grab the latest message
lines = [
    '{"level":"INFO","service":"api","msg":"ok","ts":1}',
    '{"level":"ERROR","service":"api","msg":"boom","ts":2}',
    '{"level":"ERROR","service":"db","msg":"slow","ts":3}',
    '{"level":"ERROR","service":"api","msg":"hmm","ts":4}',
    '{"level":"INFO","service":"db","msg":"ok","ts":5}',
]

summary = (
    lines
    | map(parse_json)                          # [{...}, ...]
    | filter(F.level == 'ERROR')             # [{...}, ...]
    | group_by(F.service)                      # {'api':[...], 'db':[...]}
    | items                                     # [('api',[...]), ('db',[...])]
    | map(rearrange({
        'service': z[0],
        'count':   z[1] | length,
        'last':    z[1] | sort_by(F.ts) | last | F.msg,
      }))
    | collect
)
# [{'service':'api', 'count':2, 'last':'hmm'},
#  {'service':'db',  'count':1, 'last':'slow'}]

what to notice

Every piece of the pipeline reads left-to-right like a sentence. No temp variables, no loops. If you want to add a step ("also sort by count desc"), you drop in | sort_by(F.count, descending=True). Evolution is easy because every stage has a clear input shape and output shape.

quick reference โ€” patterns

opuse caseexample
match(cases)dispatch by type/valuesee above
match_on(sel, cases)dispatch on extracted keymatch_on(F.status, [...])
rearrange(tmpl)reshape by template{'n': z[0]}
apply_functions([f,g])per-element fnszip lengths
apply_dict(d)lookup tablemap keys โ†’ values
sliding(n)windows of nmoving analyses
window_reduce(f, n)reduce per windowmoving sum
logdebug-printdrop in anywhere

Next up: Zen notation โ€” the idea that Python literals ARE the data language. โ†’