Salta al contingut
Ridgun
Català

38 El Llibre de Ridgun

La lib agent — Model Natiu d'Actors (agent, send, receive), KV Store i Rate Limiter

38.1. Per què existeix la lib agent? (El problema real i la filosofia Ridgun)

«Per què necessito una lib agent escrita des de zero en Ridgun pur en lloc d'enllaçar una llibreria de C de tota la vida?»

Aquesta és la primera pregunta que es fa qualsevol enginyer de sistemes en arribar a agent. I la resposta resumeix tota la filosofia de ridgun-libs:

Siguem sincers: compartir memòria mutable entre fils amb Mutex, RwLock i variables de condició és una fàbrica inesgotable de deadlocks i condicions de carrera.

La lib agent aprofita el Model Natiu d'Agents de Ridgun (agent, send ... to, receive from, try receive from, stop) per oferir bústies aïllades amb pas de missatges Copy-on-Write, un magatzem clau-valor concurrent (KvStore), un limitador de taxa per cubell de tokens (RateBucket) i supervisió d'estat (AgentHealth).

En un ecosistema tradicional, resoldre les tasques del domini AGENT implica arrossegar dependències externes en C, lluitar amb punters crus, conversions opaques de tipus, excepcions no verificades o pauses impredictibles del recol·lector de brossa. En Ridgun, la lib agent (src/main/ridgun/lib/agent.rg) aborda el problema amb quatre principis innegociables:

  1. Declara import agent (o import agent as alias) a l'inici del fitxer: cada lib defineix el seu espai de noms module :: agent i en mode --strict les funcions multi-paraula s'invoquen sempre amb espais naturals.
  2. Encadena les teves canonades d'esquerra a dreta amb ->: escriu entrada pas u -> pas dos -> resultat, fes servir màscares (+ _ _ +) per desestructurar seqüències i omet _ = en sentències seqüencials (o descarta amb -> ()).
  3. Recursió de cua amb TCO (Tail-Call Optimization): tots els recorreguts interns de agent es compilen a bucles natius en espai de pila $O(1)$.
  4. Memòria determinista (RMM1 / RMM2): cada estructura intermèdia s'allibera en l'instant exacte en què acaba la seva última lectura, sense Garbage Collector.

38.2. Artefactes natius, importació i catàleg complet de l'API de agent

A ridgun-libs, agent és una lib de primera classe situada a src/main/ridgun/lib/agent.rg (domini AGENT). En compilar amb stable bundle, s'empaqueta en tres formats natius:

ArtefacteRutaPropòsit
Bundle Ridguntarget/bundles/agent.rglibDistribució i importació directa amb import agent
Fitxer compilattarget/agent.rgcArtefacte intermedi verificat per ridgunc
Objecte compartit ELF64target/libagent.soBiblioteca dinàmica nativa sense dependències de C ni libc

Cada lib declara el seu propi espai de noms amb module :: agent: n'hi ha prou d'escriure import agent (o import agent as alias) a la capçalera i cridar les seves funcions amb espais naturals (la meva funcio x) i canonades -> (x la meva funcio -> resultat).

38.2.1. Resum de famílies funcionals de agent

Signatura / Funció / TipusQuè fa i quan fer-la servir
agent roundtrip / agent drain countLlança actors natius (agent), intercanvia missatges (send ... to / receive from) i executa stop
kv store new / kv store applyMagatzem clau-valor immutable per a actors (KvPut, KvGet, KvDelete, KvClear)
rate bucket new / rate bucket allowLimitador de taxa per cubell de tokens que retorna el cubell actualitzat i (allowed, remaining)
sup mark failed / sup should restartPolítiques de supervisió amb control de pressupost de reinicis (max_restarts)

38.2.2. Catàleg exhaustiu de tipus i signatures a src/main/ridgun/lib/agent.rg

A continuació tens l'inventari complet de tipus algebraics (type), operadors (op ::) i signatures de funcions (fn ::) definits a src/main/ridgun/lib/agent.rg (7 tipus, 0 operadors i 65 funcions):

Tipus algebraics i registres (type) de agent:

type AgentStatus = AgentRunning | AgentStopped | AgentTimedOut Int | AgentFailed String
type AgentReply  = ReplyInt Int | ReplyStr String | ReplyBool Bool | ReplyEmpty | ReplyStopped | ReplyTimeout Int | ReplyError String
type CounterState = cnt_val: Int
type KvCmd        = KvPut String String | KvGet String | KvDel String | KvHas String | KvCount | KvKeys | KvClear
type KvEntry      = kv_k: String, kv_v: String
type KvState      = kv_size: Int, kv_items: [KvEntry]
type LogState     = log_sep: String, log_buf: String
#Funció / Signatura (fn ::)Tipus de Retorn
1fn :: format agent status AgentStatusString
2fn :: format agent reply AgentReplyString
3fn :: is agent alive (Agent A B)Bool
4fn :: agent status (Agent A B)AgentStatus
5fn :: stop agent (Agent A B)AgentStatus
6fn :: agent stop all [Agent]()
7fn :: agent ask Agent AB
8fn :: agent try ask Agent AOption
9fn :: agent send batch Agent [A]()
10fn :: agent receive count Agent Int[B]
11fn :: agent receive count loop Agent Int [B][B]
12fn :: agent map batch Agent [A][B]
13fn :: agent list len [A] IntInt
14fn :: agent drain ready Agent[B]
15fn :: agent drain ready loop Agent [B][B]
16fn :: agent drain step Agent Option [B][B]
17fn :: ag cast opt AB
18fn :: ag unwrap try int (Option Int)AgentReply
19fn :: ag unwrap try str (Option String)AgentReply
20fn :: ask int (Agent Int Int) IntAgentReply
21fn :: ask str (Agent String String) StringAgentReply
22fn :: try poll int (Agent A Int)AgentReply
23fn :: try poll str (Agent A String)AgentReply
24fn :: ag poll int step (Agent Int Int) (Option Int) Int IntAgentReply
25fn :: ag wait int loop (Agent Int Int) Int IntAgentReply
26fn :: ask int with timeout (Agent Int Int) Int IntAgentReply
27fn :: ag poll str step (Agent String String) (Option String) Int IntAgentReply
28fn :: ag wait str loop (Agent String String) Int IntAgentReply
29fn :: ask str with timeout (Agent String String) String IntAgentReply
30fn :: ask batch int (Agent Int Int) [Int][Int]
31fn :: ask batch str (Agent String String) [String][String]
32fn :: relay int (Agent Int Int) (Agent Int Int) IntAgentReply
33fn :: relay str (Agent String String) (Agent String String) StringAgentReply
34fn :: ag counter step CounterState IntCounterState Int
35fn :: new counter agent IntAgent Int Int
36fn :: counter add (Agent Int Int) IntInt
37fn :: counter get (Agent Int Int)Int
38fn :: counter reset (Agent Int Int)Int
39fn :: ag log join String String StringString
40fn :: ag log step LogState StringLogState String
41fn :: new log agent StringAgent String String
42fn :: log append (Agent String String) StringString
43fn :: log read (Agent String String)String
44fn :: log clear (Agent String String)String
45fn :: ag kv lookup [KvEntry] StringString
46fn :: ag kv exists [KvEntry] StringBool
47fn :: ag kv put list [KvEntry] String String[KvEntry]
48fn :: ag kv del list [KvEntry] String[KvEntry]
49fn :: ag kv len [KvEntry]Int
50fn :: ag kv keys join String StringString
51fn :: ag kv keys str [KvEntry]String
52fn :: ag kv has str BoolString
53fn :: ag kv next state KvState KvCmdKvState
54fn :: ag kv next reply KvState KvCmdString
55fn :: ag kv step KvState KvCmdKvState String
56fn :: new kv agentAgent KvCmd String
57fn :: ag kv call (Agent KvCmd String) KvCmdString
58fn :: kv put (Agent KvCmd String) String StringString
59fn :: kv get (Agent KvCmd String) StringString
60fn :: kv delete (Agent KvCmd String) StringString
61fn :: kv has (Agent KvCmd String) StringBool
62fn :: ag parse int StringInt
63fn :: kv count (Agent KvCmd String)Int
64fn :: kv keys (Agent KvCmd String)String
65fn :: kv clear (Agent KvCmd String)String

38.3. Explorant agent a fons: Exemples pas a pas de cada funcionalitat

No n'hi ha prou de veure la taula de signatures: explorarem cadascuna de les funcionalitats de la lib agent mitjançant exemples progressius i executables en Ridgun. Començarem per les dues receptes tabulars d'ús quotidià i després recorrerem en detall tots els escenaris funcionals, transformacions i casos límit verificats a src/test/ridgun/test agent.rg.

38.3.1. Exemple 1: Flux principal en estil tabular amb agent

Observa com en Ridgun els signes = i els passos de transformació amb agent queden alineats com una taula neta, sense variables mutables innecessàries ni efectes laterals ocults:

import agent

fn :: probar contador actor => Int Int
      probar contador actor =
        10 new counter agent -> ag
        counter add ag 32 -> v1
        ag counter get -> v2
        ag stop agent -> ()
        (v1, v2)

38.3.2. Exemple 2: Composició segura i gestió de casos de vora

Tota funció de la lib agent està dissenyada per compondre's amb guardes booleanes | i amb la resta de les 32 libs de ridgun-libs:

import agent

fn :: consultar almacen kv => String Int
      consultar almacen kv =
        new kv agent -> kv
        kv put kv "modo" "produccion" -> ()
        kv get kv "modo" -> modo
        kv count kv -> total
        kv stop agent -> ()
        (modo, total)

38.3.3. Exemple 3: Combinadors d'actors (agent ask, try ask, send batch, map batch, drain ready), pools particionats i reactor de connexions

En aquest exemple pràctic explorem de manera exhaustiva com es comporta agent en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test spec combinators pool and net agent => ()
      test spec combinators pool and net agent =
        w1 = agent (ag double worker)
        assert int equals (agent ask w1 21) 42 "agent ask 21 -> 42"
        mb = agent map batch w1 [3, 5, 7]
        assert int equals (ag sum ints mb) 30 "agent map batch [3,5,7] -> [6,10,14] sum 30"
        agent send batch w1 [10, 20] -> ()
        rc = agent receive count w1 2
        assert int equals (ag sum ints rc) 60 "agent send batch + agent receive count -> 60"
        c_dr = new counter agent 100
        send 5 to c_dr
        send 10 to c_dr
        dr = agent drain ready c_dr
        assert int equals (ag sum ints dr) 220 "agent drain ready [105, 115] -> 220"
        agent stop all [w1, c_dr] -> ()
        assert false (is agent alive w1) "w1 stopped by agent stop all"
        assert false (is agent alive c_dr) "c_dr stopped by agent stop all"

        s0 = new counter agent 0
        s1 = new counter agent 1000
        pl = Pool 2 [s0, s1]
        assert int equals (pool size pl) 2 "pool size 2"
        assert int equals (pool ask shard pl 0 5) 5 "pool ask shard 0 -> 5"
        assert int equals (pool ask shard pl 1 7) 1007 "pool ask shard 1 -> 1007"
        assert int equals (pool ask shard pl (0 - 3) 3) 1010 "pool ask shard negative key -> shard 1"
        pool broadcast pl 10 -> ()
        all_dr = pool drain all pl
        assert int equals (ag sum ints all_dr) 1035 "pool drain all [15, 1020] -> 1035"
        pool shutdown pl -> ()
        assert false (is agent alive s0) "pool s0 stopped"
        assert false (is agent alive s1) "pool s1 stopped"

        ca = agent (ag echo conn step) (CounterState 0)
        cb = agent (ag echo conn step) (CounterState 0)
        s_eof = conn feed payload (0 - 1) ca ""
        s_err = conn handle event (0 - 1) cb 8
        kept  = reactor sweep next s_eof (reactor sweep next s_err [])
        assert int equals (agent list len kept 0) 0 "reactor sweep next prunes closed slots"
        assert false (is agent alive ca) "ca stopped on EOF"
        assert false (is agent alive cb) "cb stopped on POLLERR"
        cc = agent (ag echo conn step) (CounterState 0)
        reactor shutdown [ConnSlot (0 - 1) cc] -> ()
        assert false (is agent alive cc) "cc stopped on reactor shutdown"

38.3.4. Exemple 4: Cicle de vida d'agents (is agent alive, agent status, stop agent) i agent comptador amb estat aïllat (new counter agent)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta agent en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test agent lifecycle and counter => ()
      test agent lifecycle and counter =
        assert str eq (format agent status AgentRunning) "running" "format AgentRunning"
        assert str eq (format agent status AgentStopped) "stopped" "format AgentStopped"
        assert str eq (format agent status (AgentTimedOut 250)) "timeout after 250 ms" "format AgentTimedOut"
        assert str eq (format agent status (AgentFailed "boom")) "failed: boom" "format AgentFailed"
        assert str eq (format agent reply (ReplyInt 42)) "42" "format ReplyInt"
        assert str eq (format agent reply (ReplyStr "ok")) "ok" "format ReplyStr"
        assert str eq (format agent reply (ReplyBool True)) "true" "format ReplyBool True"
        assert str eq (format agent reply (ReplyBool False)) "false" "format ReplyBool False"
        assert str eq (format agent reply ReplyEmpty) "agent outbox empty" "format ReplyEmpty"
        assert str eq (format agent reply ReplyStopped) "agent stopped" "format ReplyStopped"
        assert str eq (format agent reply (ReplyTimeout 50)) "agent timeout after 50 ms" "format ReplyTimeout"
        assert str eq (format agent reply (ReplyError "bad")) "agent error: bad" "format ReplyError"

        c   = new counter agent 10
        assert ok (is agent alive c)
        assert str eq (format agent status (agent status c)) "running" "counter running"
        assert str eq (format agent reply (try poll int c)) "agent outbox empty" "empty outbox before send"
        assert eq (counter get c) 10
        assert eq (counter add c 5) 15
        assert eq (counter add c 7) 22
        assert str eq (format agent reply (ask int c 8)) "30" "ask int 8 -> 30"
        assert str eq (format agent reply (ask int with timeout c 12 200)) "42" "ask int with timeout -> 42"
        assert eq (counter reset c) 0
        assert eq (counter get c) 0
        bs  = ask batch int c [1, 2, 3, 4]
        assert eq (ag sum ints bs) 20

        c2  = new counter agent 100
        rel = relay int c c2 5
        assert str eq (format agent reply rel) "115" "relay int c (10+5=15) -> c2 (100+15=115)"

        st1 = stop agent c
        assert str eq (format agent status st1) "stopped" "stop agent returns AgentStopped"
        assert eq (is agent alive c) False
        assert str eq (format agent status (agent status c)) "stopped" "agent status after stop"
        assert str eq (format agent reply (ask int c 1)) "agent stopped" "ask int after stop"
        assert str eq (format agent reply (try poll int c)) "agent stopped" "try poll int after stop"
        assert str eq (format agent reply (ask int with timeout c 1 50)) "agent stopped" "ask int with timeout after stop"
        assert str eq (format agent reply (relay int c c2 1)) "agent stopped" "relay int after stop"
        c0  = new counter agent 0
        assert int equals (counter get c0) 0 "counter 0 initial"
        assert int equals (counter add c0 3) 3 "counter 0 add 3"
        stop agent c0
        stop agent c2
        assert eq (is agent alive c2) False

38.3.5. Exemple 5: Agent de bitàcola append-only (new log agent) i magatzem clau-valor en memòria per pas de missatges (new kv agent)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta agent en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

fn :: test log and kv agents => ()
      test log and kv agents =
        lg  = new log agent "\n"
        assert ok (is agent alive lg)
        assert str eq (format agent reply (try poll str lg)) "agent outbox empty" "log empty outbox"
        assert str eq (log read lg) "" "initial log empty"
        assert str eq (log append lg "alpha") "alpha" "log append alpha"
        assert str eq (log append lg "beta") "alpha\nbeta" "log append beta"
        assert str eq (format agent reply (ask str with timeout lg "gamma" 200)) "alpha\nbeta\ngamma" "ask str with timeout gamma"
        assert str eq (log read lg) "alpha\nbeta\ngamma" "log read"
        assert str eq (log clear lg) "" "log clear"
        assert str eq (log read lg) "" "log read after clear"
        b   = ask batch str lg ["one", "two"]
        assert str eq (ag join strs b) "one|one\ntwo" "ask batch str"
        lg2 = new log agent ":"
        assert str eq (format agent reply (relay str lg lg2 "three")) "one\ntwo\nthree" "relay str"
        stop agent lg
        stop agent lg2
        assert str eq (format agent reply (ask str lg "x")) "agent stopped" "ask str after stop"

        kv  = new kv agent
        assert ok (is agent alive kv)
        assert int equals (kv count kv) 0 "kv count 0"
        assert false (kv has kv "host") "kv has host false"
        assert str eq (kv put kv "host" "127.0.0.1") "127.0.0.1" "kv put host"
        assert str eq (kv put kv "port" "8080") "8080" "kv put port"
        assert int equals (kv count kv) 2 "kv count 2"
        assert ok (kv has kv "host")
        assert ok (kv has kv "port")
        assert str eq (kv get kv "host") "127.0.0.1" "kv get host"
        assert str eq (kv get kv "port") "8080" "kv get port"
        assert str eq (kv keys kv) "host,port" "kv keys"
        assert str eq (kv put kv "port" "9090") "9090" "kv overwrite port"
        assert str eq (kv get kv "port") "9090" "kv get updated port"
        assert int equals (kv count kv) 2 "kv count after overwrite"
        assert str eq (kv delete kv "host") "127.0.0.1" "kv delete host"
        assert false (kv has kv "host") "kv has host after delete"
        assert int equals (kv count kv) 1 "kv count 1"
        assert str eq (kv clear kv) "" "kv clear"
        assert int equals (kv count kv) 0 "kv count after clear"
        stop agent kv
        assert false (is agent alive kv) "kv stopped"

38.4. Sota el capó: com està construïda agent en Ridgun pur (src/main/ridgun/lib/agent.rg)

Un dels majors avantatges de ridgun-libs és que no hi ha caixes negres en C. Pots obrir src/main/ridgun/lib/agent.rg i llegir exactament com està implementada cada operació fent servir pattern matching, arbres de guardes | / |. i recursió de cua (TCO). Vegem les peces clau de la seva implementació real:

38.4.1. Implementació interna: Status & Reply Formatting

Fixa't en com agent.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

module :: agent

fn :: format agent status AgentStatus        => String
      format agent status AgentRunning       =  "running"
      format agent status AgentStopped       =  "stopped"
      format agent status (AgentTimedOut ms) =  "timeout after {ms} ms"
      format agent status (AgentFailed   msg) = "failed: {msg}"

fn :: format agent reply AgentReply        => String
      format agent reply (ReplyInt     n)  =  int to str n
      format agent reply (ReplyStr     s)  =  s
      format agent reply (ReplyBool True)  =  "true"
      format agent reply (ReplyBool False) =  "false"
      format agent reply ReplyEmpty        =  "agent outbox empty"
      format agent reply ReplyStopped      =  "agent stopped"
      format agent reply (ReplyTimeout ms) =  "agent timeout after {ms} ms"
      format agent reply (ReplyError  msg) =  "agent error: {msg}"

38.4.2. Implementació interna: Core Combinators (agent ask, agent try ask, agent send batch, agent receive count, agent map batch, agent drain ready)

Fixa't en com agent.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

fn :: agent ask Agent A   => B
      agent ask ag    msg =  ask ag msg

fn :: agent try ask Agent A   => Option
      agent try ask ag    msg =
        send msg to ag
        try receive from ag

fn :: agent send batch Agent [A]      => ()
      agent send batch _     []       =  ()
      agent send batch ag    (m:rest) =
        send m to ag
        agent send batch ag rest

fn :: agent receive count Agent Int   => [B]
      agent receive count ag    count =  agent receive count loop ag count []

fn :: agent receive count loop Agent Int [B] => [B]
      agent receive count loop ag count acc
        | count <= 0 = acc
        | otherwise =
          r = receive from ag
          agent receive count loop ag (count - 1) (acc :: [r])

fn :: agent map batch Agent [A]  => [B]
      agent map batch ag    msgs =
        agent list len msgs 0 -> n
        agent send batch ag msgs -> ()
        agent receive count ag n

fn :: agent list len [A]    Int => Int
      agent list len []     acc =  acc
      agent list len (_:xs) acc =  agent list len xs (acc + 1)

fn :: agent drain ready Agent => [B]
      agent drain ready ag    =  agent drain ready loop ag []

fn :: agent drain ready loop Agent [B] => [B]
      agent drain ready loop ag acc =
        agent drain step ag (try receive from ag) acc

fn :: agent drain step Agent Option [B] => [B]
      agent drain step _ None acc = acc
      agent drain step ag (Some r) acc = agent drain ready loop ag (acc :: [r])

38.4.3. Implementació interna: Stateful Counter Agent (new counter agent, counter add, counter get, counter reset)

Fixa't en com agent.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

fn :: ag counter step CounterState Int   => CounterState Int
      ag counter step st           delta =
        | delta == (0 - 999999999) = (CounterState 0, 0)
        | otherwise                =
          nxt = (cnt_val st) + delta
          (CounterState nxt, nxt)

fn :: new counter agent Int  => Agent Int Int
      new counter agent init =  agent (ag counter step) (CounterState init)

fn :: counter add (Agent Int Int) Int   => Int
      counter add ag              delta =
        | not (agent alive ag) = 0
        | otherwise            =
          _ = send delta to ag
          receive from ag

fn :: counter get (Agent Int Int) => Int
      counter get ag              =  counter add ag 0

fn :: counter reset (Agent Int Int) => Int
      counter reset ag              =  counter add ag (0 - 999999999)

38.4.4. Implementació interna: Stateful In-Memory Key-Value Agent (new kv agent, kv put, kv get, kv delete, kv has, kv count, kv keys, kv clear)

Fixa't en com agent.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

fn :: ag kv lookup [KvEntry] String => String
      ag kv lookup []        _      =  ""
      ag kv lookup (e:rest)  key    =
        | (kv_k e) == key = kv_v e
        | otherwise       = ag kv lookup rest key

fn :: ag kv exists [KvEntry] String => Bool
      ag kv exists []        _      =  False
      ag kv exists (e:rest)  key    =
        | (kv_k e) == key = True
        | otherwise       = ag kv exists rest key

fn :: ag kv put list [KvEntry] String String => [KvEntry]
      ag kv put list [] key val = [KvEntry key val]
      ag kv put list (e:rest) key val =
        | (kv_k e) == key = [KvEntry key val] :: rest
        | otherwise       = [e] :: (ag kv put list rest key val)

fn :: ag kv del list [KvEntry] String => [KvEntry]
      ag kv del list [] _ = []
      ag kv del list (e:rest) key =
        | (kv_k e) == key = rest
        | otherwise       = [e] :: (ag kv del list rest key)

fn :: ag kv len [KvEntry] => Int
      ag kv len []        =  0
      ag kv len (_:rest)  =  1 + (ag kv len rest)

fn :: ag kv keys join String String => String
      ag kv keys join k rest_s =
        | (str len rest_s) == 0 = k
        | otherwise             = k + "," + rest_s

fn :: ag kv keys str [KvEntry] => String
      ag kv keys str []        =  ""
      ag kv keys str (e:rest)  =  ag kv keys join (kv_k e) (ag kv keys str rest)

fn :: ag kv has str Bool  => String
      ag kv has str True  =  "1"
      ag kv has str False =  "0"

38.5. Garanties algorísmiques, memòria RMM1/RMM2 i les 5 Regles d'Or de agent

  • Garantia algorísmica i de seguretat: Zero memòria mutable compartida entre actors: cada missatge es transfereix per valor / Copy-on-Write i stop allibera la regió de l'agent de forma determinista.
  • Complexitat de pila $O(1)$ amb TCO: Tots els bucles recursius a src/main/ridgun/lib/agent.rg estan en posició de cua, de manera que ridgunc els compila a salts natius sense creixement de pila.
  • Memòria determinista (RMM1/RMM2): Zero pauses de recol·lector de brossa; totes les estructures intermèdies s'alliberen de forma determinista en sortir de l'àmbit.
  • Verificació contínua: Provada exhaustivament amb stable check i la suite automatitzada src/test/ridgun/test agent.rg.

Les 5 Regles d'Or en programar amb agent

  1. Declara import agent a l'inici del fitxer i aprofita l'equivalència entre espais i guions baixos perquè el teu codi es llegeixi com a prosa.
  2. Alinea les teves canonades de dades amb =: encadena les crides a agent en passos amb nom propi i deixa que stable format alineï les columnes.
  3. Aprofita les funcions totals de agent a les teves guardes |: comprova els valors de retorn o desembolica els ADTs amb pattern matching en lloc d'assumir que l'entrada externa sempre és vàlida.
  4. Evita copiar grans estructures si només necessites consultar un camp: recorda que en RMM2 els arguments d'entrada no incrementen ni decrementen comptadors (+0 / -0).
  5. Combina agent amb test (import test): escriu sempre assercions amb assert equals o assert true per fixar els invariants del teu domini.

Al Capítol 39 passarem a explorar en profunditat la lib pool (La lib pool — Pool Acotat de Treballadors, Round-Robin i Robatori de Treball).