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.
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
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
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.
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
# 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'
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.
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
# 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
FX.CopyToClipboard(content='hello clipboard') | run
text = FX.ReadFromClipboard() | run
# even PNG images
FX.CopyToClipboard(content=my_png_image) | run
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'
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.
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.
FX.StartHTTPServer(
routes={
'/': 'Hello Zef!',
'/health': ET.JSON(content={'status': 'ok'}),
},
port=8000,
) | run
Full treatment: chapter 21.
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
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
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.
Compose FX calls: get a random name, print "hello [name]", log it as an event.
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. โ