Salta al contingut
Ridgun
Català

Referència

Especificació del llenguatge Ridgun

Visió general

Ridgun és un llenguatge funcional, pensat per al paral·lelisme des del primer dia, amb operadors de llenguatge natural i tipatge estàtic fort. La seva sintaxi s'inspira en Haskell i els llenguatges de la família ML, i hi afegeix construccions noves per al paral·lelisme i per definir operadors llegibles per a les persones.

Característiques principals:

  • Operadors de llenguatge natural — Els operadors es llegeixen com frases en anglès
  • Paral·lelisme implícit — L'execució paral·lela és una primitiva sintàctica, no una crida a una llibreria
  • Tipatge estàtic fort — Tots els tipus es declaren a les signatures
  • Definicions per patrons — Les funcions i els operadors es defineixen amb equacions

Comentaris

Els comentaris d'una línia fan servir --:

-- This is a comment

Cadenes

Ridgun té dos tipus de literals de cadena, amb un comportament d'interpolació diferent.

Cadenes interpolades (cometes dobles)

Les cadenes entre cometes dobles admeten interpolació de codi en línia amb {expr}. L'expressió entre claus s'avalua i el seu resultat s'insereix a la cadena.

name = "Ridgun"
"Hello, {name}!"                         -- "Hello, Ridgun!"
"Sum is {sum 3 4}"                       -- "Sum is 7"
"Nested: {factorial 5} done"             -- "Nested: 120 done"

Per incloure una clau literal dins de "...", escapa-la amb una barra inversa (\{ i \}) o duplica-la ({{ i }}):

"Shell expansion: \$\{VAR:-default\}"    -- "Shell expansion: ${VAR:-default}"
"This is {{not interpolated}}"           -- "This is {not interpolated}"
"Use \{ \} or {{}} for literal braces"   -- "Use { } or {} for literal braces"

Cadenes literals (cometes simples)

Les cadenes entre cometes simples són literals: no s'hi fa cap interpolació. Tots els caràcters es tracten com a text literal, claus incloses.

'Hello, {name}!'                         -- "Hello, {name}!"
'{this is just pure chars}'              -- "{this is just pure chars}"
'No {interpolation} here'               -- "No {interpolation} here"

Resum

SintaxiInterpolacióCas d'ús
"..."Sí — {expr} (escapa les claus amb \{/\} o {{/}})Cadenes dinàmiques amb expressions incrustades
'...'No — tot literalText estàtic, plantilles, patrons d'expressions regulars

Estructura del programa

Tot el codi executable de Ridgun ha de ser dins d'una funció o d'un operador. La funció main és el punt d'entrada obligatori per executar el programa.

fn :: main => ()
      main = echo "Hello, Ridgun!"

Un fitxer font de Ridgun només pot contenir:

  • Sentències import
  • Definicions type
  • Declaracions i definicions fn i op
  • Una funció main com a punt d'entrada

No es permeten expressions soltes al nivell superior. Tot el que s'executa s'ha de cridar des de main.

Ús de la línia d'ordres

ridgunc <command> [options] <file.rg>
OrdreFunció
runCompila i executa un fitxer font
checkComprova els tipus i analitza sense executar
compileGenera un fitxer de bytecode .rgc
buildCrea un executable natiu; una biblioteca amb --lib o --so
versionMostra la versió del compilador
helpMostra l’ajuda

El format, les proves i els projectes corresponen a stable.


Funcions i operadors

Les funcions i els operadors de llenguatge natural o infixos es declaren amb una signatura de tipus que acaba en => <return_type>, seguida d'una o més equacions (cases). Els prefixos inicials fn :: i op :: són opcionals:

  • Forma canònica sense prefix (comença a la columna 0):
  list len [A]    => Int
  list len []     =  0
  list len (_:xs) =  1 + (list len xs)

  swap A with B => (B, A)
  swap x with y =  (y, x)
  • Forma explícita amb prefix (fn :: / op ::, comença a la columna 0 amb les clàusules alineades a la columna 6):
  fn :: sum Int Int => Int
        sum a   b   =  a + b

  fn :: format app err AppErr        => String
        format app err (IoError msg) =  "IO:{msg}"
  • Espais de llenguatge natural a fn ::, a les funcions natives i als noms de fitxer (FR-COMP-005):
  • Les declaracions i crides fn :: admeten noms de diverses paraules separades per espais (fn :: format app err AppErr => String, fn :: project name => String), igual que op :: i les declaracions sense prefix.
  • Les funcions natives del runtime i de test es poden cridar amb espais (assert equals a b, str len s, read file p, cmm live blocks), i les formes amb _ continuen funcionant per compatibilitat.
  • ridgunc i stable admeten de manera nativa les referències de primera classe a funcions de diverses paraules (map double val xs, ||map double val xs, filter is even xs, fold add pair 0 xs) i els noms de fitxer .rg amb espais (src/test/ridgun/test core language.rg).

Variables de tipus polimòrfiques (A..Z) i paraules clau contextuals (type, with)

Per garantir que no hi hagi cap ambigüitat lèxica entre les paraules d'un operador i els paràmetres de tipus, tant a les signatures amb prefix (op ::) com a les signatures sense prefix:

  • Tots els elements de tipus d'una signatura comencen amb una lletra majúscula (o amb [...] / (...)):
  • Les lletres majúscules soles (A..Z) indiquen variables de tipus polimòrfiques (p. ex., [A], (A, B), (A => B), swap A with B => (B, A)). Les aparicions repetides d'una variable de tipus en diversos paràmetres s'unifiquen a cada punt de crida i se substitueixen al tipus de retorn.
  • Els identificadors de diversos caràcters que comencen amb majúscula (Int, String, Bool, RExpr, Tensor) indiquen tipus concrets o definits per l'usuari.
  • Tots els elements en minúscules (o els símbols d'operador +, -, *, /, %) d'una signatura de nivell superior formen part del nom de la funció o de l'operador mixfix.
  • Paraules clau contextuals type i with: type només introdueix una declaració de tipus al nivell superior quan va seguit de UpperName =. A la resta de llocs (signatures fn :: i op ::, paraules d'operador, patrons de paràmetres, variables locals i noms de camps de registre), type i with són identificadors o paraules vàlids sense cap ambigüitat.
  • Una signatura de nivell superior amb un sol identificador inicial en minúscules seguit només de tipus abans de => és una funció prefixa (fn); qualsevol signatura amb dues o més paraules en minúscules o símbols d'operador intercalats amb tipus abans de => és un operador mixfix, infix o postfix (op).

La declaració i la definició estan separades:

  • Declaració (fn ::) — especifica el nom, els tipus dels paràmetres i el tipus de retorn
  • Definició — vincula els noms dels paràmetres i proporciona la implementació

Operadors

Els operadors són el tret distintiu de Ridgun. Tenen noms en llenguatge natural i es defineixen amb op ::. A diferència dels operadors tradicionals (+, -), els operadors de Ridgun són expressions de diverses paraules que es llegeixen com frases.

Sintaxi

op :: <natural language signature with types> -> <return_type>
      <natural language pattern with params> = <body>

Exemple bàsic

-- Declaration: "add [Int] to [Int]" returns an Int
op :: add Int to Int => Int
      add a to b = a + b

L'ús es llegiria així: add 3 to 5 → 8

Les paraules entre els paràmetres tipats (to, and, in, etc.) són paraules clau de l'operador: formen part de la seva identitat i han d'aparèixer als punts de crida.

Operadors paral·lels (||)

Els operadors poden expressar l'execució paral·lela com un concepte de primera classe amb ||:

-- Run two configs in parallel with named DAG bindings (|| expr -> var)
op :: run Conf and Conf in parallel => Res, Res
      run a and b in parallel       =  res_a, res_b
        || run my own process a     -> res_a
        || run my own process b     -> res_b

Elements clau:

  • Diversos valors de retorn — => Res, Res declara dos valors de retorn
  • Bloc || expr -> var — Vincula subcàlculs a noms de resultat (where s'ha eliminat; || unifica totes les branques paral·leles)
  • -> — Assigna el resultat d'un subcàlcul paral·lel a una variable (o a una secció de tensor -> out[i, j])
  • Les branques || independents s'executen de manera concurrent com un DAG paral·lel segons les dependències de dades

Bloc directe de canonada paral·lela (|| expr)

Quan una expressió o una funció retorna directament una tupla de càlculs paral·lels sense noms de variable intermedis, les línies || apareixen just després de =:

-- Run two processes in parallel and return their results as a tuple
op :: another way to run two processes Conf Conf in parallel => Res, Res
      another way to run two processes a b in parallel =
        || run my own process a
        || run my own process b

Cada línia || s'executa com una tasca paral·lela independent. Els resultats es recullen en l'ordre de declaració.

Sobrecàrrega d'operadors

Els operadors existents (inclosos els nadius, com +) es poden sobrecarregar per a tipus nous:

-- Overload + for Complex numbers
op :: Complex + Complex => Complex
      a + b = (real a) + (real b) + complex_i * ((imaginary a) + (imaginary b))

Precedència

Els operadors simbòlics tenen la precedència més alta: primer les matemàtiques.

Quan apareixen operadors simbòlics (com +, *) dins del cos d'un operador de llenguatge natural, s'uneixen amb més força que les paraules clau de l'operador que els envolta. Així les expressions aritmètiques s'avaluen com s'espera, sense parèntesis addicionals.

PrecedènciaCategoriaExemples
La més altaOperadors simbòlics*, /, +, -, %
Aplicació de funcióf x
Comparació==, !=, <, >
Lògicsand, or, xor, not
La més baixaOperadors de llenguatge naturaladd a to b, run a and b in parallel

Encadenament d'operadors i canonades idiomàtiques d'esquerra a dreta (->) (FR-COMP-014)

Els operadors i les funcions que transformen valors o seqüències es poden encadenar d'esquerra a dreta, de manera que les dades flueixen de manera natural de l'origen al destí, sense parèntesis niats de dreta a esquerra (h (g (f x))) ni variables intermèdies d'un sol ús (st1, st2, st3).

1. L'estil de canonada idiomàtic recomanat per Ridgun (2b)

Ridgun unifica la invocació infixa amb l'operand a l'esquerra i les etapes explícites de canonada amb -> en el mateix AST. Totes quatre formes següents són vàlides i equivalents, però la forma 2b (comença de manera natural amb un subjecte i una operació a l'esquerra, encadena les transformacions següents amb -> i vincula el resultat final amb -> target) és l'estil idiomàtic recomanat de Ridgun:

-- Recommended idiomatic style (2b):
xs map (x => x * 2) -> filter (x => x > 6) -> compact limit 14 -> results

-- Equivalent unified forms (all normalize to the exact same AST):
xs -> map (x => x * 2) -> filter (x => x > 6) -> compact limit 14 -> results  -- (2) explicit -> from subject
xs map (x => x * 2) filter (x => x > 6) compact limit 14 -> results           -- (1) contiguous infix chain
map (x => x * 2) xs -> filter (x => x > 6) -> compact limit 14 -> results     -- (3) prefix start + -> chain

Les canonades de diverses etapes també es poden escriure en línies de continuació sagnades que comencen amb ->:

raw_nums map (x => x * 3)
  -> filter (x => x >= 12)
  -> fold 0 (acc x => acc + x)
  -> total_sum

2. Regla universal del primer argument amb -> i combinadors de col·leccions

  • Regla universal per a funcions i operadors: Qualsevol valor que ve de l'esquerra de -> (o de l'esquerra d'una crida infixa x op a b) entra com a primer argument de la funció o l'operador de la dreta:
  • x -> f a b $\equiv$ x f a b $\equiv$ f x a b
  • Funciona igual per a les declaracions fn :: i les declaracions mixfix op ::, sense duplicar signatures.
  • Si una funció posterior necessita el valor de la canonada en una posició de paràmetre que no és la primera, un marcador explícit _ col·loca el valor de la canonada a _ (payload -> write_file output_path _).
  • Combinadors de col·leccions (map, ||map, filter, ||filter, fold): Accepten la col·lecció en la posició prefixa tradicional (map f xs, filter g xs, fold f init xs) o a l'esquerra d'una etapa infixa o de canonada (xs map f, xs -> map f, xs filter g, xs -> filter g, xs fold init f, xs -> fold init f).
  • Etapes d'agent (-> send to, -> ask to): Canalitzar cap a -> send to ag o -> ask to ag passa el valor de l'esquerra com a missatge ([5, 10, 15] -> sum int list -> send to worker_ag).
  • Vinculació final a una variable (-> var): Quan una sentència de canonada d'un bloc seqüencial acaba amb -> var (on var és un nom de variable local i no una funció declarada), vincula var (var = <pipeline_result>) per a la resta del bloc.

3. Màscares de seqüència en canonades (-> (+ _ _ +) ->)

Quan una etapa retorna una seqüència o una tupla de diversos valors, una etapa de màscara de seqüència (-> (+ _ _ +) ->) en filtra els elements (+ conserva una posició, _ la descarta) i desempaqueta la tupla seleccionada directament en una funció posterior de diversos arguments:

parse header packet "v0.9.9" -> (+ _ _ +) -> combine code msg -> status_line

4. Sentències amb efectes laterals sense _ = i descart explícit de la unitat (-> ())

Als blocs seqüencials, les sentències amb efectes laterals (echo, write_file, write_bytes, shell_exec, log info, print_error_lines, sys_mkdir, etc.) s'escriuen directament, sense el codi repetitiu _ =:

verify strict if enabled strict_mode input_path
s_errs -> format_type_errors -> print_error_lines
echo "[ridgunc --strict] {input_path}: {err_c} error(s) found."
sys_exit 1

Quan l'expressió final d'una funció => () crida una funció auxiliar que retorna un valor que no és () (com ara shell_exec : String => Int), afegeix -> () per avaluar l'expressió, descartar el valor de retorn i produir ():

fn :: exec built run Int => ()
      exec built run 0   =  shell_exec "./target/app" -> ()
      exec built run _   =  sys_exit 1

5. Encadenament d'operadors de seqüència sense arguments explícits

Els operadors que retornen seqüències també es poden encadenar sense arguments explícits (estil point-free):

op :: Int Int switch -> Int Int
      a   b   switch =  b   a

op :: Int Int invariant -> Int Int
      a   b   invariant =  a b switch switch

Nota: Tant -> com => s'accepten com a separadors del tipus de retorn a les capçaleres fn :: / op ::. A les expressions, -> és l'operador de canonada i de vinculació d'esquerra a dreta, mentre que => introdueix clausures lèxiques (x => expr).


Sistema de tipus

Signatures de tipus

Les signatures de tipus apareixen a les declaracions després de fn :: o op :: (o sense prefix):

fn :: <name> <type1> <type2> ... => <return_type1> <return_type2> ...
op :: <words and types mixed> => <return_type1> <return_type2> ...
  • Els paràmetres són posicionals; els tipus apareixen on s'esperen els paràmetres.
  • Sintaxi unificada de tipus de tupla i de seqüència (FR-COMP-008):
  • A Ridgun, una llista d'arguments T1 T2 ... Tn i una tupla (T1, T2, ..., Tn) representen el mateix tipus compost subjacent: una seqüència ordenada de mida fixa de valors tipats (seq).
  • Per tant, els tipus separats per espais i els tipus de tupla separats per comes són 100% intercanviables als tipus de retorn, als claudàtors de llista i a les tuples entre parèntesis:
  • Tipus de retorn després de => / ->:
      fn :: parse header String => Int Int [String String] String
      -- 100% equivalent to:
      fn :: parse header String => (Int, Int, [(String, String)], String)
  • Llistes de tuples ([T1 T2 ... Tn]):
      [String String]        -- equivalent to [(String, String)]
      [Int String Bool]      -- equivalent to [(Int, String, Bool)]
  • Tuples entre parèntesis ((T1 T2 ... Tn)):
      fn :: fst (A B) => A
            fst (a, _) =  a
  • Per què això no té cap ambigüitat sintàctica: A diferència de Haskell i ML (on la juxtaposició Maybe Int indica aplicació de tipus), els tipus parametritzats de Ridgun fan servir parèntesis explícits (Tensor(Float, 3, 3)) i les llistes fan servir claudàtors ([T]). A més, tots els elements de tipus comencen amb una lletra majúscula (A..Z, Int, String), amb [ o amb (, mentre que les paraules de funcions i operadors van en minúscules. Per tant, T1 T2 no entra mai en conflicte amb constructors de tipus ni amb paraules clau d'operadors.
  • La fletxa => (o ->) separa els tipus d'entrada del tipus de retorn.

Tipus nadius

TipusDescripció
IntEnter
RealNombre real
BoolBooleà
CharUn sol caràcter Unicode (UTF-8)
StringCadena de text (UTF-8)
ComplexNombre complex
TensorTensor mutable d'N dimensions (elements Real, CPU)
Tensor_GPUTensor mutable d'N dimensions (elements Real, GPU)

[!NOTE] Tot el text de Ridgun està codificat en UTF-8 per defecte. Char representa un sol punt de codi Unicode, i String és una seqüència de Char.

Literals booleans

Bool té dos valors primitius:

True                                     -- boolean true
False                                    -- boolean false

True i False són constructors: es poden fer servir en expressions, passar com a arguments i utilitzar en pattern matching:

fn :: is_valid Int => Bool
      is_valid n =
        | n > 0     = True
        | otherwise = False

Operadors lògics

Ridgun fa servir operadors lògics amb paraules en minúscules en lloc de símbols:

OperadorSignificatExemple
andConjunció lògicaa and b
orDisjunció lògicaa or b
xorDisjunció exclusivaa xor b
notNegació lògicanot a
result = True and False                  -- False
result = True or False                   -- True
result = True xor True                   -- False
result = not False                       -- True

Són operadors infixos de paraula clau amb la semàntica booleana habitual.

Tipus de col·lecció nadius

Llistes

Les llistes són un tipus recursiu nadiu immutable amb sintaxi pròpia. No cal definir cap ADT a mà. Les llistes són adequades per al processament funcional de dades, el càlcul simbòlic i els algorismes recursius. Per al càlcul numèric mutable, consulta Tensors més avall.

Sintaxi del tipus: [type] — una llista d'elements del tipus indicat.

fn :: sum_list [Int] => Int
fn :: reverse [Int] => [Int]

Sintaxi literal: [elem1, elem2, ...] — un literal de llista. [] és la llista buida.

fn :: my_list => [Int]
my_list = [1, 2, 3, 4, 5]

Pattern matching amb cons: (x:xs) — descompon una llista en el seu cap x i la seva cua xs. [] coincideix amb la llista buida. Fes servir _ com a comodí que coincideix però no vincula res.

fn :: sum_list [Int] => Int
      sum_list []     = 0
      sum_list (x:xs) = x + sum_list xs

fn :: list_length [Int] => Int
      list_length []     = 0
      list_length (_:xs) = 1 + list_length xs

Concatenació: :: uneix dues llistes (o seqüències).

fn :: append [Int] [Int] => [Int]
      append a b = a :: b

Tensors

Els tensors són el tipus numèric nadiu mutable d'N dimensions de Ridgun. Generalitzen els vectors, les matrius i les taules de més dimensions en un sol tipus. Els elements són Real per defecte.

Sintaxi literal: Tensor seguit de claudàtors niats. La profunditat de niament determina la dimensionalitat.

v = Tensor [1, 2, 3]                    -- 1D (vector), shape: (3)
m = Tensor [[1, 2], [5, 3]]              -- 2D (matrix), shape: (2, 2)
t = Tensor [[[1, 2], [3, 4]],            -- 3D tensor, shape: (2, 2, 2)
            [[5, 6], [7, 8]]]

Indexació: S'admet tant la indexació encadenada com la separada per comes.

val = m[0][1]        -- chained indexing: row 0, col 1
val = m[0, 1]        -- comma indexing: equivalent
row = m[0]           -- slice: entire first row

Mutació (in situ): Els tensors són mutables: els elements i les seccions es poden modificar in situ (a l'estil de PyTorch o NumPy).

m[0, 1] = 5.0                            -- set element
m[0] = Tensor [10, 20]                   -- set entire row

Contracció de tensors (@): L'operador @ fa una sumació d'Einstein. Els subíndexs de cada operand defineixen la contracció: els índexs compartits per tots els operands se sumen; els índexs únics es conserven al resultat.

-- A has shape (s, d, f), B has shape (n, d, f)
-- Shared indices: d, f → contracted (summed)
-- Result T has shape (s, n)
T = A_sdf @ B_ndf

-- Equivalent to: np.einsum('sdf,ndf->sn', A, B)

La contracció funciona amb diversos tensors en una sola expressió:

-- A(a,b,c), B(a,b,c), C(a,b,c,d)
-- Shared: a, b, c → contracted. Result R has shape (d)
R = A_abc @ B_abc @ C_abcd

-- Equivalent to: np.einsum('abc,abc,abcd->d', A, B, C)

Operacions de Hadamard (+, -, *, /): Els operadors aritmètics amb subíndexs fan operacions element a element. Els índexs coincidents alineen les dimensions; els no coincidents s'estenen per broadcasting.

-- Element-wise multiply: same shape, same indices
H = A_ij * B_ij                          -- shape (i, j)

-- Broadcasting: B has fewer dimensions
S = A_ij * B_j                           -- B broadcasts along i

-- Element-wise add
C = A_ij + B_ij

-- Element-wise divide
D = A_ij / B_j                           -- B broadcasts along i

Això elimina la necessitat de unsqueeze, expand_dims o de canviar la forma a mà: els índexs són les regles de broadcasting.

Variant per a GPU: Tensor_GPU col·loca el tensor a la memòria de la GPU per accelerar el càlcul.

g = Tensor_GPU [[1, 2], [3, 4]]
result = g_ij @ g_jk                     -- GPU-accelerated matrix multiply

Seqüències (seq)

Les seqüències són el tipus compost central de Ridgun. Una seq és una col·lecció ordenada de valors de mida fixa: en essència, una tupla tipada. Les llistes de paràmetres de funcions i operadors són seqüències implícites.

-- These parameter types form implicit sequences:
fn :: sum Int Int => Int          -- params: seq(Int, Int)
fn :: process Int Real => Bool   -- params: seq(Int, Real)

Vinculació de seqüència ((name)) — posar el nom d'un paràmetre entre parèntesis vincula tots els paràmetres com una sola variable de seqüència:

fn :: first_of_seq Int Int => Int
      first_of_seq (s)     =  s(+ _)

-- Equivalent without seq binding:
fn :: first_of Int Int => Int
      first_of s   _   => s

Selector de màscara (s(+ _)) — extreu elements per màscara, on + selecciona una posició i _ la salta:

s(+ _)                -- select 1st, skip 2nd
s(_ +)                -- skip 1st, select 2nd
s(+ _ _ _)            -- select 1st, skip 2nd-4th
s(_ _ _ +)            -- skip 1st-3rd, select 4th

Selector d'índex (s(0 3)) — extreu elements per les seves posicions, comptades des de zero:

fn :: extreme_points Int Int Int Int => Int Int
      extreme_points (s)             =  s(0 3)

Unió plana (:) — concatena dues seqüències en una sola seqüència plana:

seq1 = a, b         -- seq(a, b)
seq2 = c, d         -- seq(c, d)
seq1:seq2            -- seq(a, b, c, d)

Niament (,) — compon seqüències com a elements separats (seqüència de seqüències):

seq1, seq2           -- seq(seq(a, b), seq(c, d))

Tipus de dades algebraics (ADT)

Ridgun fa servir tipus de dades algebraics per als tipus definits per l'usuari. No hi ha classes, ni mètodes, ni blocs impl: les funcions i els operadors reben tipus com a paràmetres.

Tipus producte (registres) i sintaxi d'actualització de registres (with)

Un tipus amb camps amb nom:

type Conf  = host: String, port: Int, tls: Bool
type Point = x: Real, y: Real

S'accedeix als camps pel nom, com si fossin funcions (host c, port c), i s'actualitzen de manera funcional amb with i dos punts ::

fn :: get_host Conf => String
      get_host c    =  host c

-- Functional record update (creates a new Conf with updated fields;
-- mutates in place via Arc::try_unwrap when the record is uniquely owned):
fn :: enable_tls Conf => Conf
      enable_tls c    =  c with tls = True, port = 443

Tipus suma (variants)

Un tipus amb diversos constructors, separats per |:

type Result = Ok Int | Err String
type Option = Some Int | None
type Color = Red | Green | Blue

ADT suma genèrics i registres producte genèrics (FR-COMP-013)

Els ADT suma i els registres producte definits per l'usuari poden declarar un o més paràmetres de tipus en majúscules (A..Z o noms de variable de tipus que comencen amb majúscula) entre el nom del tipus i =:

type Result T E = Ok T | Err E
type Option A   = Some A | None
type Box A      = Box A
type Page T     = items: [T], total: Int
type Pair A B   = fst: A, snd: B
  • Les aplicacions de tipus paramètriques (Result Int String, Page User, [Box Int]) es poden fer servir a les signatures fn :: / op ::, als camps de registres niats i a les dades dels constructors d'ADT.
  • Els constructors (Ok 42, Ok "hello", Page [1, 2] 2), el pattern matching, els accessors de camps (items p, total p) i les actualitzacions funcionals de registres (p with total = 10) instancien variables de tipus noves a cada punt de crida.
  • type Option A = Some A | None està preregistrat com a ADT polimòrfic natiu quan l'usuari no l'ha declarat, de manera que try receive from ag retorna Some v o None sense necessitat de declaracions repetitives (FR-COMP-013).

Funcions anònimes i clausures lèxiques ((x => expr), (a b => expr) — FR-COMP-013)

Les funcions anònimes (lambdes) s'escriuen entre parèntesis, amb els paràmetres separats per espais, seguits de => i l'expressió del cos ((x => expr) o (a b => expr), p. ex. map (x => x + offset) xs, fold (acc x => acc + x) 0 xs), i poden capturar variables locals o paràmetres del seu àmbit lèxic envoltant:

fn :: add offset Int [Int] => [Int]
      add offset k   xs    =  map (x => x + k) xs

Cinc regles canòniques regeixen les funcions anònimes:

  1. Parèntesis obligatoris: Totes les lambdes han d'anar entre parèntesis (... => ...). Escriure x => expr sense parèntesis (com a f = x => x + 1) és un error de compilació (lambda expression must be enclosed in parentheses: '(x => expr)').
  2. Lambdes sense paràmetres ((=> expr)): Ridgun prohibeix () abans de => a les signatures de funció (fn :: main => ()). De manera coherent, una lambda sense paràmetres s'escriu (=> expr) (es crida amb thunk ()), i el seu tipus a les signatures és (=> Type). Escriure (() => expr) o (() => Type) és un error de compilació.
  3. Diversos paràmetres o desestructuració de tuples ((a b => expr) o ((a b) => expr)): (a b => expr) és una lambda de 2 paràmetres (com a fold (acc x => acc + x) 0 xs), mentre que ((a b) => expr) és una lambda d'un paràmetre que desestructura una tupla (map ((a b) => a + b) [(1, 2), (3, 4)]). Si les confons, obtens un error de tipus específic que explica la diferència.
  4. Cossos de lambda de diverses línies: Quan => és al final de la línia, el bloc sagnat de sota forma un cos de diverses sentències (o de guardes |) que es tanca amb ):
   map (x =>
     doubled = x * 2
     doubled + offset
   ) [1, 2, 3]
  1. Tipus en majúscules i valors en minúscules: (Conn => Conn) és un tipus de funció i (conn => conn) és un valor lambda. Escriure tipus en majúscules (Conn => Conn) en una expressió o paràmetres en minúscules (conn => conn) en una signatura fn :: es rebutja amb un error de compilació clar.

Les vinculacions capturades es converteixen automàticament en clausures d'aplicació parcial gestionades per RMM1/RMM2.

Tipus recursius

Els tipus es poden referenciar a si mateixos:

type List = Empty | Cons Int List
type Tree = Leaf Int | Node Tree Tree

Noms de constructor compartits entre tipus (FR-COMP-005)

Diverses declaracions type poden reutilitzar els mateixos noms de constructor de variant (com ara Error, Var, Lit, None) amb la mateixa aritat o amb una altra. El comprovador de tipus desambigua els patrons de constructor pel tipus de paràmetre que s'espera a la columna, i resol les crides a constructors per l'aritat i els tipus dels arguments i pels tipus de retorn d'ADT d'unió:

type ParseOut = Parsed Int | Error String
type DbOut    = Rows Int   | Error Int String
type MiniExpr = Var String | Lit Int
type MiniPat  = Var String | Lit Int | Wild

Extensions obertes de tipus (expand type) i fusió automàtica de clàusules fn :: (FR-COMP-005)

Els ADT suma i els registres producte existents es poden estendre amb expand type UpperName = ...:

  • Estendre un ADT suma: afegeix nous constructors de variant al tipus existent.
  • Estendre un registre producte: afegeix nous camps amb nom (field: Type) i genera automàticament les seves funcions d'accés.
  • Variants amb nom de tipus sense noms de constructor redundants: a les declaracions de suma type i expand type, una variant el nom de la qual és només un tipus nadiu (String, Int, Real/Float, Bool, Char) sense més arguments de tipus al darrere representa directament valors d'aquest tipus (sense escriure Int Int o String String). Qui crida pot passar valors sense constructor (describe "hej", describe 42) o crides explícites al constructor (describe (String "hej"), describe (Int 42)), i les equacions poden coincidir tant amb literals ("admin", 0) com amb patrons tipats ((String s), (Int n)).
  • Fusió automàtica de clàusules fn :: / op :: (sense expand fn): tornar a declarar un fn :: o op :: existent amb noves clàusules de patró les fusiona automàticament amb la definició de funció existent, i col·loca les clàusules de patró específiques de constructor o de literal abans de qualsevol clàusula alternativa general (_ o variable):
type Something = String | Int | MyStruct Float Int
expand type Something = Bool

fn :: describe something Something            => String
      describe something "admin"              =  "special_admin"
      describe something 0                    =  "zero_int"
      describe something (String   s)         =  "str:{s}"
      describe something (Int      n)         =  "int:{n}"
      describe something (Bool     b)         =  "bool:{b}"
      describe something (MyStruct ratio cnt) =  "struct:{ratio}:{cnt}"
type AppErr = IoError String | Timeout Int

fn :: format app err AppErr        => String
      format app err (IoError msg) =  "IO:{msg}"
      format app err (Timeout ms)  =  "TO:{ms}"
      format app err _             =  "UNKNOWN"

expand type AppErr = AuthError String | DbError Int String

fn :: format app err AppErr              => String
      format app err (AuthError user)    =  "AUTH:{user}"
      format app err (DbError   code db) =  "DB:{code}:{db}"

type AppConfig        = host: String, port: Int
expand type AppConfig = tls: Bool

Espais de noms de mòdul, accés qualificat i visibilitat estricta de les importacions per mòdul (FR-COMP-012)

Cada fitxer .rg o paquet .rglib defineix un espai de noms de mòdul (derivat del nom del fitxer sense extensió o d'una capçalera explícita module :: <name>) i pot importar altres mòduls amb import <mod> o import <mod> as <alias>.

  1. Mòduls de diversos fitxers (module :: <name>):
  • Diversos fitxers que declaren la mateixa capçalera module :: <name> (per exemple, orm.rg i orm_changeset.rg comencen tots dos amb module :: orm) comparteixen un únic espai de noms lògic orm i veuen directament els tipus, els constructors, els camps de registre i les funcions dels altres.
  1. Accés qualificat (mod.Type, mod.Constructor, mod.field, mod.fn):
  • Els tipus exportats, els constructors d'ADT suma, els accessors de camps de registres producte, les actualitzacions de registres (with) i les funcions es poden referenciar amb un prefix <mod_or_alias>. (http.Config, db.Status, http.Ok 200, http.port cfg, http.check status cfg).
  1. Resolució estricta de noms no qualificats per mòdul, en 3 nivells:
  • Cada mòdul M (inclòs el mòdul arrel de l'aplicació) resol els tipus, els constructors, els camps de registre, les funcions i les crides de diverses paraules no qualificats estrictament contra el graf d'importacions del mateix M, en tres nivells de prioritat:
  • Nivell 1 — Mòdul propi (M): Les declaracions del mateix M (en tots els fitxers que comparteixen module :: M) tenen sempre la prioritat més alta.
  • Nivell 2 — Importacions directes de M: Si M no defineix el nom, ridgunc busca als mòduls que M importa directament (import D). Si una sola importació directa D defineix el nom, es resol sense ambigüitat a la declaració de D, encara que altres mòduls de la unitat de compilació (dependències transitives, mòduls germans o l'aplicació arrel) també defineixin el mateix nom. Si dues o més importacions directes de M defineixen el mateix nom i M no el defineix, les referències no qualificades es rebutgen com a ambigües (__ambiguous__.<name>) i s'han de qualificar (d1.<name> o d2.<name>).
  • Nivell 3 — Importacions transitives de M: Només es consulten quan ni el nivell 1 ni el nivell 2 defineixen el nom. Per tant, una importació directa a M (import json) oculta netament una funció auxiliar transitiva (codec.json_get_str, que solstice importa internament) sense obligar solstice ni M a canviar el nom de les seves crides o a qualificar-les.
  • Aïllament dels mòduls germans no importats i de l'aplicació arrel: Les declaracions definides només en mòduls que M no ha importat (ni directament ni transitivament), incloses les llibreries germanes importades per l'aplicació arrel o les declaracions de la mateixa aplicació arrel quan es compila una llibreria importada M, són invisibles per a la cerca no qualificada dins de M (__unimported__.<name>).

Gramàtica unificada de = ((none), |, ||) i variables locals (FR-COMP-003)

Totes les definicions i vinculacions de Ridgun, ja sigui una clàusula de funció (fn ::), una clàusula d'operador (op ::), una variable local immutable (x =), una variable local mutable (mut x =) o una branca de guarda (| cond = / | otherwise =), acaben amb = i accepten exactament les mateixes quatre formes de bloc o d'expressió a la dreta:

Prefix després de =Semàntica d'execucióSignificat
<expr> en líniaUna sola expressió (amb línies opcionals || expr -> var a sota)Avalua <expr> directament (i resol les vinculacions de DAG paral·lel || que hi hagi a sota).
(none) (línies sagnades)Seqüencial (de dalt a baix)Avalua les sentències per ordre (mut, assignacions, efectes laterals); l'última expressió és el resultat.
| <cond> = ...Condicional (guardes i arbres de decisió niats)Avalua les condicions booleanes de dalt a baix fins a la primera branca True (o | otherwise =).
|| <expr>Paral·lela (DAG de green threads o tupla)Avalua les branques || de manera concurrent (|| expr -> var per a nodes de DAG amb nom, || expr per a resultats de tupla paral·lela).

1. Les guardes (|) sempre segueixen un = (funcions, operadors, variables locals i arbres de decisió niats)

Els blocs de guardes (| cond = ... i | otherwise = ...) sempre apareixen després d'un =, cosa que unifica les clàusules de funció, les clàusules d'operador, les definicions de variables locals i els arbres condicionals niats:

Clàusules de funció i d'operador amb guardes

fn :: abs Int => Int
      abs n =
        | n < 0     = 0 - n
        | otherwise = n

op :: clamp Int between Int and Int => Int
      clamp x   between lo  and hi  =
        | x < lo    = lo
        | x > hi    = hi
        | otherwise = x

Assignació de variables locals amb guardes

Com que | és vàlid després de qualsevol =, les variables locals es poden vincular de manera condicional sense necessitat d'una paraula clau if/else ni d'una funció auxiliar:

discount =
  | is_vip      = 20
  | total > 100 = 10
  | otherwise   = 0

Blocs seqüencials de diverses línies, arbres de decisió profunds i tancaments niats (|. / ||.)

Cada branca de guarda acaba amb =, de manera que el seu cos pot ser un bloc seqüencial ((none)), un bloc paral·lel (||) o un altre bloc de guardes | niat, cosa que permet arbres de decisió arbitraris (de 3 o 4 nivells de profunditat, o més) dins de | cond = i | otherwise =.

  • Tancar nivells niats (|. i ||.):
  • En blocs simples d'un nivell o al final d'una funció o d'un arbre, no cal cap element de tancament.
  • Quan un subbloc niat | (o ||) acaba i l'execució torna a un nivell exterior on hi ha més branques o sentències a sota, |. (per a |) o ||. (per a ||) tanca explícitament aquell nivell niat.
  • Són vàlides tant la col·locació en línia al final de l'última línia (| otherwise = val |., l'estil compacte canònic) com la col·locació a la línia següent (|.):
fn :: evaluate_payment Account Customer Payment => String
      evaluate_payment account customer payment =
        | is_locked account   = "REJECTED_ACCOUNT_LOCKED"
        | amount payment <= 0 = "ERROR_INVALID_AMOUNT"
        | otherwise =
            | tier customer == "ENTERPRISE" =
                | amount payment > 50000 =
                    | has_dual_signature payment = "APPROVED_ENTERPRISE_DUAL_SIG"
                    | tenure customer > 10       = "RISK_COMMITTEE_REVIEW"
                    | otherwise                  = "REJECTED_ENTERPRISE_LIMIT" |.
                | country payment != "US" =
                    | is_whitelisted (country payment) = "APPROVED_ENTERPRISE_INTL"
                    | otherwise                        = "HELD_COMPLIANCE_INTL" |.
                | otherwise = "APPROVED_ENTERPRISE_STANDARD" |.
            | tier customer == "VIP" =
                | amount payment > 10000 =
                    | has_2fa payment = "APPROVED_VIP_2FA"
                    | otherwise       = "REQUEST_VIP_SMS" |.
                | otherwise = "APPROVED_VIP_DIRECT" |.
            | otherwise =
                | amount payment > 1000 =
                    | has_2fa payment and risk payment < 30 = "APPROVED_RETAIL_2FA"
                    | otherwise                             = "REJECTED_RETAIL_RISK" |.
                | otherwise = "APPROVED_RETAIL_LOW_AMOUNT"

fn :: charge_order Payment => Int
      charge_order payment =
        || fetch_base_fee payment     -> base_fee
        || compute_risk_score payment -> risk_score ||.
        surcharge =
          | risk_score > 80 = 50
          | otherwise       = 0 |.
        base_fee + surcharge

2. Immutables per defecte (x = ...) o variables mutables explícites (mut x = ...)

Dins dels blocs seqüencials, les variables locals segueixen l'Opció 1:

  • Vinculació immutable (x = <rhs>):
  • Declara una nova variable local immutable x.
  • Intentar reassignar x = ... més endavant al mateix àmbit sense una declaració mut x = ... prèvia és un error de compilació.
  • Declaració mutable (mut x = <rhs>) i reassignació (x = <new_rhs>):
  • mut x = <rhs> declara x com a mutable a l'àmbit actual i la inicialitza.
  • Les reassignacions posteriors de x al mateix àmbit s'escriuen simplement x = <new_rhs> (sense repetir mut).
  • Amb RMM2, reassignar x = <new_rhs> incrementa la nova estructura (+1) abans de decrementar l'antiga (-1, en cascada cap als seus fills si el seu comptador arriba a 0).
fn :: accumulate Int => Int
      accumulate n =
        factor = 2
        mut acc = n * factor
        acc = acc + 10
        acc =
          | acc > 100 = 100
          | otherwise = acc
        acc

3. Pattern matching sobre literals i constructors d'ADT

fn :: factorial Int => Int
      factorial 0   =  1
      factorial n   =  n * factorial (n - 1)

fn :: unwrap Result    => Int
      unwrap (Ok n)    =  n
      unwrap (Err msg) =  0

fn :: length List => Int
      length Empty         =  0
      length (Cons _ rest) =  1 + length rest

fn :: sum_tree Tree              => Int
      sum_tree (Leaf n)          =  n
      sum_tree (Node left right) =  sum_tree left + sum_tree right

Primitives de paral·lelisme (||)

Ridgun unifica tota l'execució paral·lela sota || (where s'ha eliminat):

Vinculacions de DAG paral·lel amb nom (|| expr -> var)

fn :: compute_total Int Int => Int
      compute_total a   b   =  x + y + z
        || heavy_calc a     -> x
        || heavy_calc b     -> y
        || combine x y      -> z
  • Totes les línies || expr -> var sota una expressió formen un graf acíclic dirigit (DAG) paral·lel.
  • El compilador analitza les dependències de variables entre les línies || (x i y no tenen dependències i s'executen en paral·lel a l'onada 1; z depèn de x i y i s'executa a l'onada 2, abans d'avaluar x + y + z).
  • També s'admeten les escriptures paral·leles en seccions de tensor (|| expr -> out[i, j]).

Blocs directes de canonada paral·lela (|| expr)

op :: run Conf and Conf in parallel => Res, Res
      run a    and b    in parallel =
        || run_process a
        || run_process b

Cada línia || s'executa com un green thread paral·lel separat, i els resultats es retornen com una tupla en l'ordre de declaració (o s'uneixen quan el bloc és dins d'un bloc seqüencial).

Canals tipats

Per a fluxos de dades i topologies de productor i consumidor entre green threads, Ridgun proporciona canals MPMC tipats (Channel):

Funció o operador natiuSignaturaFinalitat
new_channel() => ChannelCrea un canal tipat sense límit
new_bounded_channelInt => ChannelCrea un canal limitat amb capacitat de contrapressió
send_throughT Channel => ()Envia un valor a un canal (es bloqueja si és limitat i està ple)
receive_fromChannel => TRep el valor següent d'un canal
try_receive_fromChannel => OptionConsulta no bloquejant que retorna Some v o None

Primitives d'E/S

Ridgun proporciona funcions natives per a la sortida al terminal.

echo

Escriu al terminal un nombre arbitrari de cadenes, separades per espais i seguides d'un salt de línia.

fn :: echo String... => ()

El sufix ... a l'últim tipus de paràmetre el marca com a variàdic: accepta un o més arguments d'aquest tipus.

echo "hello"                            -- prints: hello
echo "hello" "world" "!"               -- prints: hello world !
echo "result: {result}"                 -- prints: result: 42

out

Un operador que escriu una cadena directament al terminal i la retorna (cosa que permet encadenar).

op :: String out => String
"hello" out                              -- prints: hello
"step 1 done" out                        -- prints: step 1 done

-- Chaining: out returns the string, so it can be passed onward
fn :: log_and_process String => String
      log_and_process s = process (s out) and report

Resum

FuncióTipusFinalitat
echoString... => ()Escriu cadenes amb espais i un salt de línia
outString => StringEscriu una cadena i la retorna per poder encadenar

Llibreria estàndard

Les llibreries s'importen amb import:

import test

Tests unitaris (lib/test.rg)

Una llibreria de tests senzilla que fa servir echo per a la sortida al terminal.

Assercions:

OperadorFinalitat
assert Int equals IntIgualtat d'enters
assert Real equals RealIgualtat de reals
assert String equals StringIgualtat de cadenes
assert Bool is trueEl booleà és cert
assert Bool is falseEl booleà és fals

Estructura dels tests:

import test

fn :: main => ()
      main =
        describe "Arithmetic" does

          test "sum adds correctly" does
            assert (sum 3 4) equals 7

          test "factorial base case" does
            assert (factorial 0) equals 1

Sortida al terminal:

═══ Arithmetic ═══
● sum adds correctly
  ✓ PASS
● factorial base case
  ✓ PASS

Resum dels elements sintàctics

ElementSintaxiFinalitat
Comentari-- textComentari d'una línia
Declaració de funciófn :: name types => returnDeclara la signatura de tipus d'una funció
Definició de funcióname params = bodyDefineix la implementació d'una funció
Declaració d'operadorop :: words types words => returnDeclara un operador de llenguatge natural
Definició d'operadorwords params words = bodyDefineix la implementació d'un operador
Diversos retorns=> type, typeDeclara diversos valors de retorn
Paràmetres variàdicsType...Accepta un o més arguments d'un tipus
Literal de tensorTensor [[1, 2], [3, 4]]Crea un tensor d'N dimensions
Indexació de tensort[0, 1] o t[0][1]Accedeix als elements d'un tensor
Mutació de tensort[0, 1] = 5.0Modifica els elements d'un tensor in situ
Contracció d'EinsteinA_ij @ B_jkContracció de tensors sobre índexs compartits
Operació de HadamardA_ij * B_ijAritmètica element a element amb alineació d'índexs
echoecho "a" "b" "c"Escriu cadenes al terminal
out"text" outTreu una cadena pel terminal
Vinculació local immutablex = exprVincula una variable local immutable (= admet (none), |, ||)
Vinculació local mutablemut x = exprDeclara una variable local mutable (es reassigna amb x = new_expr)
Bloc de guardes| cond = exprBranca de guarda condicional després de = (admet | niats)
Bloc de DAG paral·lel|| expr -> varSubcàlculs paral·lels amb resultats de DAG amb nom
Bloc de canonada paral·lela|| exprExecució paral·lela que retorna una tupla
Sobrecàrrega d'operadorsop :: type + type => typeSobrecarrega operadors existents per a tipus nous
Literals booleansTrue, FalseValors primitius booleans
Operadors lògicsand, or, xor, notOperadors lògics amb paraules en minúscules
Punt d'entradafn :: main => ()Punt d'entrada obligatori del programa

Format canònic del codi font (stable format i eines d'edició)

Només estètic, no és un requisit del compilador (FR-TOOL-001): L'analitzador lèxic i l'analitzador sintàctic de Ridgun no són sensibles a l'alineació horitzontal en columnes. Per a ridgunc, els fitxers font .rg sense alinear i els alineats en columnes són 100% equivalents. Les regles següents defineixen la disposició estètica canònica que apliquen stable format i el ridgun-mode de GNU Emacs (C-c C-f).

  1. Grup d'alineació per signatura (fn :: / op ::):
  • Cada declaració fn :: o op :: comença un grup d'alineació que inclou la seva signatura, les seves clàusules de pattern matching i les seves vinculacions || expr -> var, i que acaba a la següent declaració de nivell superior (fn, op, type, import, global, context, shared, hardware, @).
  1. Alineació en columnes dels elements de la part esquerra ($C_0 = 6$):
  • fn :: i op :: fan 6 caràcters d'amplada, de manera que l'element $0$ de la part esquerra de la signatura comença a la columna $C_0 = 6$.
  • Cada posició d'element $i \in [0, N-1]$ abans de =>/-> (tractant "...", '...', [...] i (...) com a elements únics) té amplada $W_i = \max(\text{len}(\text{sig_tok}i), \max{\text{clause} : \text{len}(\text{clause_tok}_i) \le 18} \text{len}(\text{clause_tok}_i))$ i comença a la columna $C_i = C_{i-1} + W_{i-1} + 1$. Els patrons de clàusula de més de 18 caràcters es tracten com a excepcions: no estiren $W_i$ per a la resta del grup i simplement sobresurten a la seva pròpia línia amb 1 espai.
  • Les clàusules de definició corresponents se sagnen a la columna $6$ ($C_0$), de manera que el nom de la funció o les paraules de l'operador i cada patró d'argument s'alineen verticalment sota la posició corresponent de la signatura.
  1. Alineació vertical de =>, = i -> ($C_{\text{arrow}}$):
  • Sigui $C_{\text{arrow}} = \max\big(C_{N-1} + W_{N-1} + 1,; \max_{\text{pipe_bind}}(8 + 3 + \text{len}(\text{pipe_lhs}) + 1)\big)$ i $C_{\text{ret}} = C_{\text{arrow}} + \text{len}(\text{arrow}) + 1$.
  • A la signatura, => es col·loca a $C_{\text{arrow}}$ i el tipus de retorn a $C_{\text{ret}}$.
  • A les clàusules d'una línia (<lhs> = <rhs>), a les clàusules de bloc de diverses línies (<lhs> =) i a les clàusules encapçalades per guardes (<lhs> = seguit de | cond = ...), = es col·loca a $C_{\text{arrow}}$ (alineat just a sota de =>); a les clàusules d'una línia, <rhs> comença a $C_{\text{ret}}$.
  • A les vinculacions de DAG paral·lel (|| <lhs> -> <rhs>), || comença a la columna $8$ (6 + 2), -> es col·loca a $C_{\text{arrow}}$ i <rhs> comença a $C_{\text{ret}}$.
  • Alternativa compacta (> 4 paràmetres o > 80 columnes): Si una declaració té més de 4 posicions de paràmetre o la seva línia de signatura alineada superaria els 80 caràcters, el farciment horitzontal en columnes es desactiva per a aquest grup i els elements se separen amb un sol espai (" "), tot conservant el sagnat de clàusula a la columna 6.
  1. Sagnat estructural (desplaçament de 2 espais):
  • Declaracions de nivell superior (fn, op, type, import, global, context, shared, hardware, @): columna 0.
  • Clàusules de funció o d'operador sota una signatura: columna 6.
  • Guardes (| cond = expr) i canonades paral·leles (|| expr / || expr -> var): prev_indent + 2 (columna 8 dins d'una clàusula, o indent + 2 dins d'una vinculació local o d'una guarda niada).
  • Línies del cos d'un bloc seqüencial després d'una línia que acaba en =: prev_indent + 2.

Operacions d'ordre superior seqüencials i paral·leles sobre col·leccions (FR-COMP-001, FR-COMP-014)

Ridgun proporciona els combinadors de col·leccions seqüencials habituals (map, filter, fold) juntament amb combinadors paral·lels multinucli explícits amb el prefix || (||map, ||filter). Tots cinc combinadors admeten tant la sintaxi idiomàtica infixa o de canonada d'esquerra a dreta (xs map f -> filter g -> fold 0 h -> res) com la sintaxi prefixa tradicional (map f xs):

NatiuFormes prefixa i de canonadaModel d'execució
mapxs map f / xs -> map f / map f xsSeqüencial: avalua f x estrictament d'esquerra a dreta al fil que fa la crida.
`map`
filterxs filter p / xs -> filter p / filter p xsSeqüencial: avalua el predicat p x estrictament d'esquerra a dreta al fil que fa la crida.
`filter`
foldxs fold init f / xs -> fold init f / fold f init xsPlegat seqüencial per l'esquerra: acumula acc = f acc x d'esquerra a dreta a partir de init.

E/S literal de fitxers, fluxos de bytes i funcions natives POSIX (FR-VM-001, FR-VM-002)

  1. E/S literal de fitxers i de fluxos de bytes (FR-VM-001):
  • read_file : String => String llegeix el contingut dels fitxers literalment (conserva \r\n i la presència o absència exacta del salt de línia final, i mai no executa el preprocessador d'import .rg del compilador sobre fitxers de dades).
  • load_rg_source : String => String només el fan servir les eines del compilador quan carreguen fitxers font .rg amb inserció d'import.
  • read_bytes : String => [Int] i write_bytes : String [Int] => () llegeixen i escriuen fluxos de bytes binaris en brut (0..255, inclosos 0x00 NUL i 0x80..0xFF).
  1. Funcions natives POSIX de sistema de fitxers, processos i seguretat (FR-VM-002):
  • Sistema de fitxers: list_dir, file_lstat, sha256_file, sys_mkdir, sys_chmod.
  • Processos i entorn: sys_exec, sys_chdir, sys_getcwd, sys_getenv, sys_setenv, sys_exit.
  • Seguretat i separació de privilegis: sys_getuid, sys_getgid, sys_username, sys_user_groups, sys_lookup_uid_gid, sys_setresgid, sys_setgroups, sys_setresuid, sys_execve, sys_read_pass.
  1. E/S PAL multiplataforma, sondeig, primitives de processos, ADT de destinació i quotes declaratives with timeout (FR-COMP-007):
  • ADT de plataforma de destinació predeclarats:
  • type SysOs = Linux | MacOS | Windows (sys os => SysOs, obert a extensió amb expand type SysOs = ...)
  • type SysArch = X86_64 | Arm64 (sys arch => SysArch, obert a extensió amb expand type SysArch = ...)
  • Literals amb unitat de durada: Els literals enters accepten els sufixos _ms (* 1), _s (* 1000) i _min (* 60000) (p. ex., 500_ms, 5_s, 2_min), que complementen els sufixos d'unitat de bytes (_KB, _MB, _GB).
  • Capa 1 — Primitives PAL multiplataforma per a les llibreries del sistema .rglib (posix.rglib / io.rglib):
  • sys os => SysOs i sys arch => SysArch (ADT suma del sistema operatiu i l'arquitectura de destinació en temps de compilació)
  • sys open mode String String => Int (path, mode portable "r", "w", "a", "rw", "w+", "a+" -> descriptor/identificador >= 0 o -errno, traduït segons el sistema operatiu de destinació)
  • sys open String Int Int => Int (path, flags normalitzats de la PAL de Ridgun (0 = lectura, 1 = escriptura, 2 = lectura i escriptura, 64 = crear, 512 = truncar, 1024 = afegir al final, 2048 = no bloquejant), mode -> fd >= 0 o -errno)
  • sys close Int => Int (fd -> 0 o -errno)
  • sys read fd Int Int => String (fd, max_bytes -> cadena llegida de fd)
  • sys write fd Int String => Int (fd, data -> bytes escrits o -errno)
  • sys poll Int Int Int => Int (fd, events normalitzats de la PAL de Ridgun (1 = POLLIN/lectura, 4 = POLLOUT/escriptura), timeout_ms -> > 0 revents quan està a punt, 0 si s'esgota el temps, < 0 amb -errno; fa servir sys remaining ms quan timeout_ms < 0 dins de with timeout)
  • sys terminate Int Bool => Int (pid, force -> finalització portable de processos, ordenada o forçada) i sys kill Int Int => Int (pid, senyal normalitzat -> 0 o -errno)
  • sys call Int Int Int Int Int Int Int => Int (nr, a1..a6 -> via d'escapament de baix nivell per fer crides al sistema amb l'ABI del sistema operatiu de destinació)
  • sys time ms => Int i sys sleep ms Int => () (rellotge monòton en mil·lisegons i espera)
  • sys deadline ms => Int i sys remaining ms => Int (termini lèxic actiu i mil·lisegons restants, o -1 quan no hi ha límit)
  • Capa 2 — Quota d'execució declarativa with timeout <duration> do:
  • Igual que with region 64_KB do estableix una quota lèxica de bytes, with timeout 500_ms do (o la forma combinada with region 64_KB, timeout 500_ms do) estableix un termini d'execució lèxic (sys_time_ms + duration_ms) al context del fil actual i restaura el termini anterior en sortir del bloc:
       with timeout 500_ms do
         rem = sys remaining ms
         rc  = sys poll fd 1 rem
         rc
  1. Compilació de llibreries reutilitzables (.rglib i .so ELF64) i stable extensible (FR-COMP-006, FR-TOOL-003):
  • ridgunc build --lib <file.rg> -o <out.rglib> i ridgunc build --so <file.rg> -o <lib.so> (o stable built lib / stable new --lib <name>) compilen mòduls sense necessitat de fn :: main => () i conserven totes les funcions, els operadors i els tipus exportats.
  • stable detecta les ordres personalitzades fn :: task <words...> => String i les extensions fn :: plugins => [String] (plugins/*.rg, .stable/plugins/*.rg), que es llisten amb stable tasks i stable plugins.

Gestió de memòria de Ridgun (RMM): RMM1 (CMM) i RMM2 (FR-COMP-002)

Ridgun gestiona la memòria del heap (String, [a], (a, b), ADT, Tensor) de manera determinista, sense un recol·lector de brossa (GC) per traçat, mitjançant la gestió de memòria de Ridgun (Ridgun Memory Management, RMM):

  1. Política RMM1 — Gestió de memòria compilada (CMM lineal + condicional):
  • S'aplica quan el flux de control i les funcions cridades es coneixen estàticament en temps de compilació, sobre vinculacions no niades:
  • CMM lineal (rg_cmm_move / rg_cmm_drop): El compilador determina en temps de compilació la sentència exacta de l'últim ús de cada variable local, mou les variables retornades (rg_cmm_move(&mut lv_x)) i emet un alliberament incondicional (rg_cmm_drop(&mut lv_x)) just després de l'últim ús d'una variable quan no queda viu cap possible àlies.
  • CMM condicional (rg_cmm_drop_unless / ptr(a) != ptr(c)): Quan una crida amb bifurcació estàtica (c = choose_list cond a b) retorna una de diverses entrades candidates i una o més candidates continuen vives al costat de c, el compilador construeix la taula d'àlies possibles d'ancestres (RCmmAlias) i emet una comprovació d'identitat de punter d'1 cicle a la sentència de l'últim ús estàtic de cada variable:
       a = make_a ()               -- Allocates M1
       b = make_b ()               -- Allocates M2
       c = choose_list cond a b    -- b dies here while c is live -> if ptr(b) != ptr(c) { free(b) }
       print_list a                -- a dies here while c is live -> if ptr(a) != ptr(c) { free(a) }
       consume_list c              -- c dies here (no live peers) -> free(c) (unconditional)
  1. Política RMM2 — Assignació i recompte d'estructures en cascada (política activa):
  • Unifica la gestió de memòria entre funcions, retorns amb bifurcació (choose_list), contenidors niats (xs = [a, b, ..., z], xs[3] = a), variables locals mutables (mut x = ...) i execució multifil:
  • Escalars primitius (Int, Real, Bool, ()): No es compten mai; es copien per valor a la pila o als registres.
  • Arguments d'entrada de funció (pats): No es toquen mai (+0 en entrar, -0 en sortir).
  • Variables locals internes (x = ... / mut x = ...): Reben +1 en l'assignació (x = expr, xs[3] = a). En una reassignació mutable (x = new_expr), new_expr rep +1 abans que l'antiga x rebi -1.
  • Final de funció o sortida d'àmbit:
  • Les variables locals internes no retornades reben primer -1: si rc == 0, s'allibera el bloc pare i es propaga -1 en cascada, una a una, a cada estructura filla directa (alliberant recursivament qualsevol filla el rc de la qual arribi a 0).
  • Una variable local interna retornada (return a) rep -1 (o es mou amb rg_cmm_move), i encara que el seu rc sigui 0 en trànsit, es preserva perquè l'assignació de qui crida (c = make_pair ()) hi afegeixi +1 (0 + 1 = 1). Quan es retorna un argument d'entrada (choose_list cond a b), no s'ha tocat dins de la funció (rc = 1), de manera que el c = choose_list cond a b de qui crida hi afegeix +1 (1 + 1 = 2, compartit entre a i c).
  1. Política RMM3 (reservada):
  • Reservada per a una futura política d'optimització estàtica agressiva.
  • Consulta docs/etc/cmm_architecture.md (al repositori de Ridgun) per a l'especificació completa de RMM1 i RMM2.