๐Ÿ 

the FX catalog tour

a tourist walk through the effect library.

There are ~100 FX types in Zef. We won't cover every single one โ€” we'll hit the daily-use ones and leave signposts for the rest.

Print & Log

FX.Print(content='๐ŸŒฟ hello') | run

FX.Log(content=ET.UserJoined(name='Alice')) | run
# structured log; query later

FX.QueryLog(filter=ET.UserJoined, limit=10) | run
# returns list of logged entities

Log is structured

Unlike print, FX.Log records an entity โ€” a typed value โ€” into an in-memory log store. You can query it by type later. Perfect for audit trails, debug spelunking, or feeding a live monitoring dashboard.

FX.SubscribeLog(level='info', filter=ET.UserJoined) | run
# live stream of matching log entries

HTTPRequest

resp = FX.HTTPRequest(
    url='https://api.example.com/users',
    method='POST',
    headers={'Content-Type': 'application/json'},
    body='{"name":"Alice","age":30}',
) | run

resp['status']        # 200
resp['body']          # response body
resp['headers']       # headers dict
resp['duration']      # request duration

Think of it as requests.request(...) but as a value first.

CurrentTime

t = FX.CurrentTime() | run
# Time(1702567890.123456)  โ€” 10ns resolution, i64 ticks

Time is a first-class Zef type. Compare, sort, subtract:

start = FX.CurrentTime() | run
# ... do stuff ...
end = FX.CurrentTime() | run
elapsed = end - start    # a Duration

Random (with distributions)

# bounded integer
dice = FX.Random(tp=Int32, distribution=ET.Box(lower=1, upper=6)) | run

# uniform [0, 1)
u = FX.Random(tp=Float64, distribution=ET.Box(lower=0, upper=1)) | run

# gaussian
iq = FX.Random(tp=Float64, distribution=ET.Gaussian(mean=100, std=15)) | run

# exponential (e.g. waiting times)
wait = FX.Random(tp=Float64, distribution=ET.Exponential(rate=0.5)) | run

# categorical weighted
choice = FX.Random(tp=Int32, distribution=ET.Categorical(weights=[1, 3, 5])) | run

# a UID
uid = FX.Random(tp=UID) | run
# '๐Ÿƒ-6dc2ec6a470d33ec919b'

why explicit distributions?

Most random APIs give you random() โˆˆ [0, 1) and leave you to compose the rest. Zef makes the distribution an explicit, typed parameter so there's no ambiguity. "Uniform 0-100 inclusive" is a specific thing you can write.

Timers (recurring ticks via topics)

tick_topic = ET.Topic(generate_uid())

# start a timer that publishes 'tick' every second
timer = FX.StartTimer(
    interval=1,                      # seconds (or 'hourly', 'daily', ...)
    target=tick_topic,
    content='tick',
) | run

# eventually:
FX.StopTimer(timer=timer) | run

Timers run on Zef's shared Tokio runtime โ€” no Python thread, no GIL, zero CPU when idle. They publish to a topic; subscribe with an actor or FX.SubscribeFX to react.

Scheduled start:

FX.StartTimer(
    interval=60,
    starting_at=Time('2026-01-15 09:00:00 +0000'),
    target=topic,
    content='daily_reminder',
) | run

File I/O

# save any zef value to a .zef binary file
data = Array([ET.Note(text='hi'), ET.Note(text='bye')])
FX.SaveToLocalFile(content=data, path='/tmp/notes.zef', overwrite_existing=True) | run

# load it back
loaded = FX.LoadFromLocalFile(path='/tmp/notes.zef') | run
# round-trips cleanly โ€” same bytes you saved

Clipboard

FX.CopyToClipboard(content='hello clipboard') | run
text = FX.ReadFromClipboard() | run

# even PNG images
FX.CopyToClipboard(content=my_png_image) | run

Publish & Subscribe

topic = ET.Topic('๐Ÿƒ-88d4f6a0bbbe12345678')

# subscribe a pipeline to the topic
FX.SubscribeFX(
    topic=topic,
    op=log,                   # just print everything that comes in
) | run

# publish some messages
FX.Publish(target=topic, content='hello') | run
FX.Publish(target=topic, content='world') | run
# ๐Ÿชต: 'hello'
# ๐Ÿชต: 'world'

Create / Update / Query DBs

db = FX.CreateDB(type=DictDatabase, persistence='in_memory') | run

FX.UpdateDB(db=db, insert={'name': 'alice', 'count': 0}) | run
FX.QueryDB(db=db, get='name') | run      # 'alice'

# convenience shorthand
db['count'] = 42
db['count']         # 42

Much more on databases in chapter 16.

Signals

counter = FX.CreateSignal(value=0, history=100) | run
FX.SetSignal(signal=counter, value=42) | run
FX.ReadSignal(signal=counter) | run              # 42

# atomic update (no race condition)
FX.UpdateSignal(signal=counter, transform=add(1)) | run

Full treatment: chapter 17.

HTTP server

FX.StartHTTPServer(
    routes={
        '/':        'Hello Zef!',
        '/health':  ET.JSON(content={'status': 'ok'}),
    },
    port=8000,
) | run

Full treatment: chapter 21.

WebSocket client (outbound)

inbox  = ET.Topic(generate_uid())
outbox = ET.Topic(generate_uid())

client = FX.StartWebSocketClient(
    url='wss://echo.example.com/ws',
    in_stream_into=[inbox],
    out_stream_from=[outbox],
) | run

FX.Publish(target=outbox, content='ping') | run

FX.StopWebSocketClient(client=client) | run

Actors & workers

Brief mention โ€” full coverage ahead:

actor  = FX.StartActor(input=topic, initial_state=0, handler=h) | run
worker = FX.StartWorkerProcess() | run
result = FX.SendCommandToWorkerProcess(
    process=worker,
    command=FX.Print(content='from isolated worker')
) | run

the "role" system (tiny but useful)

FX.SetRole(role='user_python_process') | run
role = FX.GetRole() | run   # 'user_python_process'

Processes can declare their role (tokolosh, zpython, user_python_process, etc.). Other parts of the runtime can query this and adjust behavior.

the mental map

Pure FX โ”‚ effects about the "here and now" FX.Print โ”‚ output to stdout FX.Log โ”‚ add structured event to log store FX.CurrentTime โ”‚ timestamp FX.Random โ”‚ generate a value FX.CopyToClipboard โ”‚ clipboard I/O Comms โ”‚ effects about external processes FX.HTTPRequest โ”‚ fire an HTTP call FX.HTTPRequestSSE โ”‚ stream server-sent events FX.StartWebSocketClient out to a WS server Persistence โ”‚ effects about saving / loading FX.CreateDB / UpdateDB / QueryDB FX.SaveToLocalFile / FX.LoadFromLocalFile Reactive โ”‚ effects about pub/sub FX.Publish / FX.SubscribeFX FX.StartTimer / FX.StopTimer Servers โ”‚ effects about running services FX.StartHTTPServer FX.StartActor / FX.StopActor FX.StartWorkerProcess FX.CreateSignal / FX.UpdateSignal / FX.ReadSignal

write a mini pipeline

Compose FX calls: get a random name, print "hello [name]", log it as an event.

solution
names = ['Ada', 'Bea', 'Carl']
i = FX.Random(tp=Int32, distribution=ET.Box(lower=0, upper=len(names)-1)) | run
name = names[i]

FX.Print(content=f'hello {name}') | run
FX.Log(content=ET.Greeted(name=name, at=FX.CurrentTime() | run)) | run

Next up: @zef_function โ€” turn your Python into a content-addressed ZefOp. โ†’