Rat docs

Tail — calling Python

Rat's dispatch primitive — name.method(args) — doesn't care whether the callee is a builtin, a service, a DB handle, or a function in another runtime. Tail is the populator that wires a Python (or Node, R, anything) module into the global namespace, so a page can call weather.predict(city) exactly the way it calls auth.login(...). Every example below is running for real — the server has a Python worker open on site/lang/python/.

Live quote

wisdom.quote() runs Python on every render

site/lang/python/wisdom.py exposes a quote() function backed by random.choice. Refresh this page and the value changes — proof that the call hits the live worker, not a cached snapshot.

# site/lang/python/wisdom.py
import random
_QUOTES = ['...', '...', '...']

def quote():
    return random.choice(_QUOTES)
# site/pages/.../tail.rat
> server
quote_now: wisdom.quote()

> page
<blockquote> [quote_now]
Result
The bet: one dispatch primitive collapses five mechanisms.

Live text analysis

text.analyze runs at SSR time

text.analyze(s) returns a dict from Python; Rat receives it as a regular object. Member access ([sample_analysis.chars]) reads the result inline. The whole call took one JSON round-trip into the Python subprocess.

# site/lang/python/text.py
from collections import Counter

def analyze(s):
    letters = [c.lower() for c in s if c.isalpha()]
    top = Counter(letters).most_common(3)
    return { "chars": len(s), "words": len(s.split()), "vowels": sum(1 for c in letters if c in 'aeiou'), "top": [{"letter": l, "count": n} for l, n in top], }
> server
sample: 'Hello from Python, dispatched through Rat.'
sample_analysis: text.analyze(sample)

> page
<p> chars: [sample_analysis.chars]
<p> words: [sample_analysis.words]
<p> vowels: [sample_analysis.vowels]
Result

input: "Hello from Python, dispatched through Rat."

chars: 42

words: 6

vowels: 10

top letters:

  • h — 5 times
  • o — 4 times
  • t — 4 times

Interactive: analyze any string

Service-backed round trip per click

A text_demo service in main.rat wraps the Python call and stores the result on its own frame. The button below hits run_text_analysis(demo_text) via __ratCall; the top-level wrapper invokes text_demo.analyze(s) which calls into Python and writes the result onto the service singleton. Refresh to see the values below update — service mutations persist across requests but aren't pushed back as a reactive patch yet.

# main.rat
> service [text_demo]
result: {chars: 0, words: 0, vowels: 0, top: []}

analyze[s]
    result << text.analyze(s)

# this page
> page
demo_text: 'rat is small but mighty'
out >> text_demo.result

<input value[demo_text] on_input[demo_text << event.target.value]>
<button on_click[text_demo.analyze(demo_text)]> analyze
<p> chars: [out.chars]   words: [out.words]   vowels: [out.vowels]
Result

chars: 0 — words: 0 — vowels: 0

Two more helpers

reverse() and slugify() — pure Python

Each function in text.py becomes a member on the text namespace. No registration ceremony, no decorator — the Tail worker discovers them via a one-shot discover message at boot.

# site/lang/python/text.py
def reverse(s):
    return s[::-1]

def slugify(s):
    # trim, lowercase, hyphenate non-alnum runs
    ...
> server
sample_reversed: text.reverse(sample)
slug_demo: text.slugify('My Brilliant Post #42!')
Result

reversed: .taR hguorht dehctapsid ,nohtyP morf olleH

slug of "My Brilliant Post #42!": my-brilliant-post-42

How it composes

No new grammar, no compiler change

When the engine evaluates text.analyze(s), the lookup hits Globals["text"] just like auth or main_db. The only thing Tail adds is a Callable.Go closure that ships args to a long-lived Python subprocess and reads the result back over a JSON pipe. Eval doesn't know — and doesn't need to know — that the callee lives across an IPC boundary.

Setup

One-time, per project

Rat scans lang/python/ at boot, spawns a worker, sends a discover message, and registers each module as a namespace. Requirements (if any) install from lang/python/requirements.txt via rat install. The two demo modules used here are stdlib-only so no install step was needed.

site/
  lang/
    python/
      requirements.txt      # optional, pip format
      text.py               # def analyze(s): ...
      wisdom.py             # def quote(): ...

Next: Inverse Tail — same dispatch shape, but for JS libraries running in the browser (with a live Chart.js demo).