Referencia
Especificación da linguaxe Ridgun
Visión xeral
Ridgun é unha linguaxe funcional, pensada desde o inicio para o paralelismo, con operadores en linguaxe natural e tipado estático forte. A súa sintaxe inspírase nas linguaxes da familia de Haskell e ML, e introduce construcións novas para o paralelismo e para definir operadores que se len con facilidade.
Características principais:
- Operadores en linguaxe natural — Os operadores lense como frases en inglés
- Paralelismo implícito — A execución paralela é unha primitiva sintáctica, non unha chamada a unha biblioteca
- Tipado estático forte — Todos os tipos se declaran nas sinaturas
- Definicións por patróns — As funcións e os operadores defínense mediante ecuacións
Comentarios
Os comentarios dunha soa liña usan --:
-- This is a comment
Cadeas
Ridgun ten dous tipos de literal de cadea, que se comportan de xeito distinto coa interpolación.
Cadeas interpoladas (comiñas dobres)
As cadeas entre comiñas dobres admiten interpolación de código en liña con {expr}. A expresión entre chaves avalíase e o seu resultado insírese na cadea.
name = "Ridgun"
"Hello, {name}!" -- "Hello, Ridgun!"
"Sum is {sum 3 4}" -- "Sum is 7"
"Nested: {factorial 5} done" -- "Nested: 120 done"
Para incluír unha chave literal dentro de "...", escápaa cunha barra invertida (\{ e \}) ou duplícaa ({{ e }}):
"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"
Cadeas crúas (comiñas simples)
As cadeas entre comiñas simples son crúas: non se fai ningunha interpolación. Todos os caracteres se tratan como texto literal, incluídas as chaves.
'Hello, {name}!' -- "Hello, {name}!"
'{this is just pure chars}' -- "{this is just pure chars}"
'No {interpolation} here' -- "No {interpolation} here"
Resumo
| Sintaxe | Interpolación | Uso |
|---|---|---|
"..." | Si — {expr} (escapa as chaves con \{/\} ou {{/}}) | Cadeas dinámicas con expresións incrustadas |
'...' | Non — todo é literal | Texto estático, modelos, patróns de expresións regulares |
Estrutura do programa
Todo o código executable de Ridgun debe estar dentro dunha función ou dun operador. A función main é o punto de entrada obrigatorio para executar o programa.
fn :: main => ()
main = echo "Hello, Ridgun!"
Un ficheiro fonte de Ridgun só pode conter:
- instrucións
import - definicións
type - declaracións e definicións
fneop - unha función
maincomo punto de entrada
Non se permiten expresións soltas no nivel superior. Todo o que se executa ten que chamarse desde main.
Uso da liña de comandos
ridgunc <command> [options] <file.rg>
| Orde | Función |
|---|---|
run | Compila e executa un ficheiro fonte |
check | Comproba os tipos e analiza sen executar |
compile | Xera un ficheiro de bytecode .rgc |
build | Crea un executable nativo; unha biblioteca con --lib ou --so |
version | Mostra a versión do compilador |
help | Mostra a axuda |
O formato, as probas e os proxectos corresponden a stable.
Funcións e operadores
As funcións e os operadores en linguaxe natural ou infixos decláranse cunha sinatura de tipos que remata en => <return_type>, seguida dunha ou máis ecuacións (cases). Os prefixos iniciais fn :: e op :: son opcionais:
- Forma canónica sen prefixo (comeza na 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 con prefixo (
fn ::/op ::, comeza na columna 0 coas cláusulas aliñadas na 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}"
- Espazos de linguaxe natural en
fn ::, funcións integradas e nomes de ficheiro (FR-COMP-005): - As declaracións e as chamadas
fn ::admiten nomes de varias palabras separadas por espazos (fn :: format app err AppErr => String,fn :: project name => String), igual queop ::e as declaracións sen prefixo. - As funcións integradas do runtime e de probas pódense chamar con espazos (
assert equals a b,str len s,read file p,cmm live blocks), e as formas con_seguen sendo válidas por compatibilidade. - As referencias de primeira clase a funcións de varias palabras (
map double val xs,||map double val xs,filter is even xs,fold add pair 0 xs) e os nomes de ficheiro.rgcon espazos (src/test/ridgun/test core language.rg) están admitidos de forma nativa enridguncestable.
Variables de tipo polimórficas (A..Z) e palabras chave contextuais (type, with)
Para garantir que non haxa ningunha ambigüidade léxica entre as palabras dos operadores e os parámetros de tipo, tanto nas sinaturas con prefixo (op ::) como nas sen prefixo:
- Todo elemento de tipo nunha sinatura comeza por letra maiúscula (ou por
[...]/(...)): - As letras maiúsculas soltas (
A..Z) denotan variables de tipo polimórficas (por exemplo,[A],(A, B),(A => B),swap A with B => (B, A)). As aparicións repetidas dunha variable de tipo entre parámetros unifícanse en cada punto de chamada e substitúense no tipo de retorno. - Os identificadores de varios caracteres con maiúscula inicial (
Int,String,Bool,RExpr,Tensor) denotan tipos concretos ou definidos polo usuario. - Todo elemento en minúsculas (ou símbolo de operador
+,-,*,/,%) nunha sinatura de nivel superior forma parte do nome da función ou do operador mixfix. - Palabras chave contextuais
typeewith:typesó introduce unha declaración de tipo no nivel superior cando vai seguida deUpperName =. En calquera outro lugar (sinaturasfn ::eop ::, palabras de operadores, patróns de parámetros, variables locais e nomes de campos de rexistro),typeewithson identificadores ou palabras válidos, sen ambigüidade. - Unha sinatura de nivel superior con un único identificador inicial en minúsculas seguido só de tipos antes de
=>é unha función prefixa (fn); calquera sinatura con dúas ou máis palabras en minúsculas ou símbolos de operador intercalados con tipos antes de=>é un operador mixfix, infixo ou postfixo (op).
A declaración e a definición van separadas:
- Declaración (
fn ::) — indica o nome, os tipos dos parámetros e o tipo de retorno - Definición — liga os nomes dos parámetros e proporciona a implementación
Operadores
Os operadores son o trazo distintivo de Ridgun. Nómeanse en linguaxe natural e defínense con op ::. A diferenza dos operadores tradicionais (+, -), os de Ridgun son expresións de varias palabras que se len como frases.
Sintaxe
op :: <natural language signature with types> -> <return_type>
<natural language pattern with params> = <body>
Exemplo básico
-- Declaration: "add [Int] to [Int]" returns an Int
op :: add Int to Int => Int
add a to b = a + b
O uso lerase así: add 3 to 5 → 8
As palabras entre parámetros tipados (to, and, in, etc.) son palabras chave do operador: forman parte da súa identidade e teñen que aparecer nos puntos de chamada.
Operadores paralelos (||)
Os operadores poden expresar a execución paralela como un concepto de primeira clase usando ||:
-- 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
Elementos clave:
- Varios valores de retorno —
=> Res, Resdeclara dous valores de retorno - Bloque
|| expr -> var— Liga subcálculos a nomes de resultado (whereelimínase;||unifica todas as ramas paralelas) ->— Asigna o resultado dun subcálculo paralelo a unha variable (ou a unha porción de tensor,-> out[i, j])- As ramas
||independentes execútanse concorrentemente como un DAG paralelo baseado nas dependencias de datos
Bloque de tubaxe paralela directa (|| expr)
Cando unha expresión ou función devolve directamente unha tupla de cálculos paralelos sen nomes de variable intermedios, as liñas || aparecen xusto despois do =:
-- 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 liña || execútase como unha tarefa paralela independente. Os resultados recóllense na orde de declaración.
Sobrecarga de operadores
Os operadores existentes (incluídos os integrados como +) pódense sobrecargar para tipos novos:
-- Overload + for Complex numbers
op :: Complex + Complex => Complex
a + b = (real a) + (real b) + complex_i * ((imaginary a) + (imaginary b))
Precedencia
Os operadores simbólicos teñen a maior precedencia: primeiro as matemáticas.
Cando aparecen operadores simbólicos (como +, *) dentro do corpo de operadores en linguaxe natural, ligan con máis forza que as palabras chave do operador que os rodea. Así, as expresións aritméticas avalíanse como se espera, sen parénteses adicionais.
| Precedencia | Categoría | Exemplos |
|---|---|---|
| Máxima | Operadores simbólicos | *, /, +, -, % |
| Aplicación de función | f x | |
| Comparación | ==, !=, <, > | |
| Lóxicos | and, or, xor, not | |
| Mínima | Operadores en linguaxe natural | add a to b, run a and b in parallel |
Encadeamento de operadores e tubaxes idiomáticas de esquerda a dereita (->) (FR-COMP-014)
Os operadores e as funcións que transforman valores ou secuencias pódense encadear de esquerda a dereita, de xeito que os datos flúan con naturalidade da orixe ao destino sen parénteses aniñadas de dereita a esquerda (h (g (f x))) nin variables intermedias desbotables (st1, st2, st3).
1. O estilo de tubaxe idiomático recomendado en Ridgun (2b)
Ridgun unifica a invocación infixa co operando á esquerda e as etapas explícitas de tubaxe -> na mesma AST. Aínda que as catro formas de abaixo son válidas e equivalentes, a forma 2b (comezar de xeito natural cun suxeito e unha operación á esquerda, encadear as transformacións seguintes con -> e ligar o resultado final con -> target) é o estilo idiomático recomendado en 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
As tubaxes de varias etapas tamén se poden escribir en liñas de continuación sangradas que comezan con ->:
raw_nums map (x => x * 3)
-> filter (x => x >= 12)
-> fold 0 (acc x => acc + x)
-> total_sum
2. Regra universal do primeiro argumento de -> e combinadores de coleccións
- Regra universal para funcións e operadores: calquera valor que veña da esquerda de
->(ou da esquerda dunha chamada infixax op a b) entra como primeiro argumento da función ou do operador da dereita: x -> f a b$\equiv$x f a b$\equiv$f x a b- Funciona sen problemas tanto coas declaracións
fn ::como coas declaracións mixfixop ::, sen duplicar sinaturas. - Se unha función posterior precisa o valor da tubaxe nun parámetro que non sexa o primeiro, un marcador explícito
_coloca o valor en_(payload -> write_file output_path _). - Combinadores de coleccións (
map,||map,filter,||filter,fold): aceptan a colección tanto na posición prefixa tradicional (map f xs,filter g xs,fold f init xs) como á esquerda dunha etapa infixa ou de tubaxe (xs map f,xs -> map f,xs filter g,xs -> filter g,xs fold init f,xs -> fold init f). - Etapas de axente (
-> send to,-> ask to): ao enviar a tubaxe a-> send to agou-> ask to ag, o valor da esquerda pásase como mensaxe ([5, 10, 15] -> sum int list -> send to worker_ag). - Ligazón a unha variable terminal (
-> var): cando unha instrución de tubaxe dun bloque secuencial remata en-> var(ondevaré o nome dunha variable local e non unha función declarada), ligavar(var = <pipeline_result>) para o resto do bloque.
3. Máscaras de secuencia en tubaxes (-> (+ _ _ +) ->)
Cando unha etapa devolve unha secuencia ou tupla de varios valores, unha etapa de máscara de secuencia (-> (+ _ _ +) ->) filtra os seus elementos (+ conserva unha posición, _ descártaa) e desempaqueta a tupla seleccionada directamente nunha función posterior de varios argumentos:
parse header packet "v0.9.9" -> (+ _ _ +) -> combine code msg -> status_line
4. Instrucións con efectos secundarios sen _ = e descarte explícito de unit (-> ())
Nos bloques secuenciais, as instrucións con efectos secundarios (echo, write_file, write_bytes, shell_exec, log info, print_error_lines, sys_mkdir, etc.) escríbense directamente, sen a repetición de _ =:
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
Cando a expresión final dunha función => () chama a unha función auxiliar que devolve un valor distinto de () (como shell_exec : String => Int), engade -> () para avaliar a expresión, descartar o seu valor de retorno e producir ():
fn :: exec built run Int => ()
exec built run 0 = shell_exec "./target/app" -> ()
exec built run _ = sys_exit 1
5. Encadeamento point-free de operadores de secuencia
Os operadores que devolven secuencias tamén se poden encadear en estilo 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: tanto
->como=>se aceptan como separadores do tipo de retorno nas cabeceirasfn ::/op ::. Nas expresións,->é o operador de tubaxe e de ligazón de esquerda a dereita, mentres que=>introduce clausuras léxicas(x => expr).
Sistema de tipos
Sinaturas de tipos
As sinaturas de tipos aparecen nas declaracións despois de fn :: ou op :: (ou sen prefixo):
fn :: <name> <type1> <type2> ... => <return_type1> <return_type2> ...
op :: <words and types mixed> => <return_type1> <return_type2> ...
- Os parámetros son posicionais; os tipos aparecen onde se esperan os parámetros.
- Sintaxe unificada de tipos de tupla e secuencia (
FR-COMP-008): - En Ridgun, unha lista de argumentos
T1 T2 ... Tne unha tupla(T1, T2, ..., Tn)representan o mesmo tipo composto subxacente: unha secuencia ordenada e de tamaño fixo de valores tipados (seq). - Polo tanto, os tipos separados por espazos e os tipos de tupla separados por comas son 100 % intercambiables nos tipos de retorno, nos corchetes de lista e nas tuplas entre parénteses:
- Tipos de retorno despois de
=>/->:
fn :: parse header String => Int Int [String String] String
-- 100% equivalent to:
fn :: parse header String => (Int, Int, [(String, String)], String)
- Listas de tuplas (
[T1 T2 ... Tn]):
[String String] -- equivalent to [(String, String)]
[Int String Bool] -- equivalent to [(Int, String, Bool)]
- Tuplas entre parénteses (
(T1 T2 ... Tn)):
fn :: fst (A B) => A
fst (a, _) = a
- Por que isto non ten ningunha ambigüidade sintáctica: a diferenza de Haskell/ML (onde a xustaposición
Maybe Intdenota aplicación de tipo), en Ridgun os tipos parametrizados usan parénteses explícitas (Tensor(Float, 3, 3)) e as listas usan corchetes ([T]). Ademais, todo elemento de tipo comeza por letra maiúscula (A..Z,Int,String),[ou(, mentres que as palabras de funcións e operadores van en minúsculas. Por iso,T1 T2nunca entra en conflito con construtores de tipo nin con palabras chave de operadores. - A frecha
=>(ou->) separa os tipos de entrada do tipo de retorno.
Tipos integrados
| Tipo | Descrición |
|---|---|
Int | Enteiro |
Real | Número real |
Bool | Booleano |
Char | Un só carácter Unicode (UTF-8) |
String | Cadea de texto (UTF-8) |
Complex | Número complexo |
Tensor | Tensor N-dimensional mutable (elementos Real, CPU) |
Tensor_GPU | Tensor N-dimensional mutable (elementos Real, GPU) |
[!NOTE] Todo o texto en Ridgun está codificado en UTF-8 por defecto.
Charrepresenta un único punto de código Unicode, eStringé unha secuencia deChar.
Literais de Bool
Bool ten dous valores primitivos:
True -- boolean true
False -- boolean false
True e False son construtores: pódense usar en expresións, pasar como argumentos e usar en pattern matching:
fn :: is_valid Int => Bool
is_valid n =
| n > 0 = True
| otherwise = False
Operadores lóxicos
Ridgun usa operadores lóxicos con palabras en minúsculas en vez de símbolos:
| Operador | Significado | Exemplo |
|---|---|---|
and | Conxunción lóxica | a and b |
or | Disxunción lóxica | a or b |
xor | OU exclusivo | a xor b |
not | Negación lóxica | not a |
result = True and False -- False
result = True or False -- True
result = True xor True -- False
result = not False -- True
Son operadores infixos de palabra chave coa semántica booleana habitual.
Tipos de colección nativos
Listas
As listas son un tipo recursivo integrado e inmutable con sintaxe propia. Non fai falta definir ningún ADT á man. As listas son axeitadas para o procesamento funcional de datos, o cálculo simbólico e os algoritmos recursivos. Para o cálculo numérico mutable, consulta Tensores máis abaixo.
Sintaxe de tipo: [type] — unha lista de elementos do tipo indicado.
fn :: sum_list [Int] => Int
fn :: reverse [Int] => [Int]
Sintaxe literal: [elem1, elem2, ...] — un literal de lista. [] é a lista baleira.
fn :: my_list => [Int]
my_list = [1, 2, 3, 4, 5]
Pattern matching con cons: (x:xs) — descompón unha lista na súa cabeza x e na súa cola xs. [] coincide coa lista baleira. Usa _ como comodín que coincide pero non liga ningún valor.
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ón: :: une dúas listas (ou secuencias).
fn :: append [Int] [Int] => [Int]
append a b = a :: b
Tensores
Os tensores son o tipo numérico N-dimensional mutable nativo de Ridgun. Xeneralizan vectores, matrices e arrays de máis dimensións nun único tipo. Os elementos son Real por defecto.
Sintaxe literal: Tensor seguido de corchetes aniñados. A profundidade do aniñamento determina a dimensionalidade.
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ón: admítense tanto a indexación encadeada como a separada por comas.
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ón (in-place): os tensores son mutables: os elementos e as porcións pódense modificar in-place (ao estilo de PyTorch/NumPy).
m[0, 1] = 5.0 -- set element
m[0] = Tensor [10, 20] -- set entire row
Contracción de tensores (@): o operador @ realiza a suma de Einstein. Os subíndices de cada operando definen a contracción: os índices compartidos por todos os operandos súmanse; os únicos sobreviven no resultado.
-- 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)
A contracción funciona con varios tensores nunha soa expresión:
-- 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)
Operacións de Hadamard (+, -, *, /): os operadores aritméticos con subíndices realizan operacións elemento a elemento. Os índices coincidentes aliñan dimensións; os non coincidentes fan 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
Isto elimina a necesidade de unsqueeze, expand_dims ou de cambiar a forma á man: os índices son as regras de broadcasting.
Variante para GPU: Tensor_GPU coloca o tensor na memoria da GPU para acelerar o cálculo.
g = Tensor_GPU [[1, 2], [3, 4]]
result = g_ij @ g_jk -- GPU-accelerated matrix multiply
Secuencias (seq)
As secuencias son o tipo composto central de Ridgun. Un seq é unha colección ordenada e de tamaño fixo de valores: en esencia, unha tupla tipada. As listas de parámetros de funcións e operadores son implicitamente secuencias.
-- These parameter types form implicit sequences:
fn :: sum Int Int => Int -- params: seq(Int, Int)
fn :: process Int Real => Bool -- params: seq(Int, Real)
Ligazón de secuencia ((name)) — envolver o nome dun parámetro entre parénteses liga todos os parámetros como unha única variable seq:
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 por máscara (s(+ _)) — extrae elementos mediante unha máscara, onde + selecciona unha posición e _ sáltaa:
s(+ _) -- select 1st, skip 2nd
s(_ +) -- skip 1st, select 2nd
s(+ _ _ _) -- select 1st, skip 2nd-4th
s(_ _ _ +) -- skip 1st-3rd, select 4th
Selector por índice (s(0 3)) — extrae elementos polas súas posicións, contadas desde cero:
fn :: extreme_points Int Int Int Int => Int Int
extreme_points (s) = s(0 3)
Unión plana (:) — concatena dúas secuencias nunha única secuencia plana:
seq1 = a, b -- seq(a, b)
seq2 = c, d -- seq(c, d)
seq1:seq2 -- seq(a, b, c, d)
Aniñamento (,) — compón secuencias como elementos distintos (secuencia de secuencias):
seq1, seq2 -- seq(seq(a, b), seq(c, d))
Tipos de datos alxébricos (ADT)
Ridgun usa tipos de datos alxébricos para os tipos definidos polo usuario. Non hai clases, métodos nin bloques impl: as funcións e os operadores reciben os tipos como parámetros.
Tipos produto (rexistros) e sintaxe de actualización de rexistros (with)
Un tipo con campos con nome:
type Conf = host: String, port: Int, tls: Bool
type Point = x: Real, y: Real
Aos campos accédese polo nome, como funcións (host c, port c), e actualízanse de forma funcional con with e dous puntos ::
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
Tipos suma (variantes)
Un tipo con varios construtores, separados por |:
type Result = Ok Int | Err String
type Option = Some Int | None
type Color = Red | Green | Blue
ADT suma xenéricos e rexistros produto xenéricos (FR-COMP-013)
Os ADT suma e os rexistros produto definidos polo usuario poden declarar un ou máis parámetros de tipo en maiúsculas (A..Z ou nomes de variables de tipo con maiúscula inicial) entre o nome do tipo e =:
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
- As aplicacións de tipos paramétricos (
Result Int String,Page User,[Box Int]) pódense usar en sinaturasfn ::/op ::, en campos de rexistro aniñados e nos datos que levan os construtores de ADT. - Os construtores (
Ok 42,Ok "hello",Page [1, 2] 2), o pattern matching, os accesores de campo (items p,total p) e as actualizacións funcionais de rexistros (p with total = 10) instancian variables de tipo novas en cada punto de chamada. type Option A = Some A | Noneestá rexistrado de antemán como ADT polimórfico integrado cando o usuario non o declarou, así quetry receive from agdevolveSome vouNonesen necesidade de declaracións repetitivas (FR-COMP-013).
Funcións anónimas e clausuras léxicas ((x => expr), (a b => expr) — FR-COMP-013)
As funcións anónimas (lambdas) escríbense entre parénteses, con parámetros separados por espazos seguidos de => e da expresión do corpo ((x => expr) ou (a b => expr), por exemplo map (x => x + offset) xs, fold (acc x => acc + x) 0 xs), e poden capturar variables locais ou parámetros do seu ámbito léxico envolvente:
fn :: add offset Int [Int] => [Int]
add offset k xs = map (x => x + k) xs
Cinco regras canónicas rexen as funcións anónimas:
- Parénteses obrigatorias: toda lambda debe ir entre parénteses
(... => ...). Escribirx => exprsen parénteses (comof = x => x + 1) é un erro de compilación (lambda expression must be enclosed in parentheses: '(x => expr)'). - Lambdas sen parámetros (
(=> expr)): Ridgun prohibe()antes de=>nas sinaturas de función (fn :: main => ()). En coherencia con iso, unha lambda sen parámetros escríbese(=> expr)(invócase comothunk ()), e o seu tipo nas sinaturas é(=> Type). Escribir(() => expr)ou(() => Type)é un erro de compilación. - Varios parámetros fronte a desestruturación de tuplas (
(a b => expr)fronte a((a b) => expr)):(a b => expr)é unha lambda de 2 parámetros (comofold (acc x => acc + x) 0 xs), mentres que((a b) => expr)é unha lambda de 1 parámetro que desestrutura unha tupla (map ((a b) => a + b) [(1, 2), (3, 4)]). Confundilas produce un erro de tipos específico que explica a diferenza. - Corpos de lambda de varias liñas: cando
=>está ao final da liña, o bloque sangrado de debaixo forma un corpo de varias instrucións (ou de gardas|) que se pecha con):
map (x =>
doubled = x * 2
doubled + offset
) [1, 2, 3]
- Tipos en maiúsculas fronte a valores en minúsculas:
(Conn => Conn)é un tipo de función e(conn => conn)é un valor lambda. Escribir tipos en maiúsculas(Conn => Conn)nunha expresión ou parámetros en minúsculas(conn => conn)nunha sinaturafn ::recházase cun erro de compilación claro.
As ligazóns capturadas elévanse automaticamente a clausuras de aplicación parcial xestionadas por RMM1/RMM2.
Tipos recursivos
Os tipos poden referirse a si mesmos:
type List = Empty | Cons Int List
type Tree = Leaf Int | Node Tree Tree
Nomes de construtor compartidos entre tipos (FR-COMP-005)
Varias declaracións type poden reutilizar os mesmos nomes de construtor de variante (como Error, Var, Lit, None) coa mesma aridade ou con outra distinta. O verificador de tipos desambigua os patróns de construtor mediante o tipo de parámetro esperado da columna, e resolve as chamadas a construtores pola aridade e os tipos dos argumentos e polos tipos de retorno ADT de unión:
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
Extensións abertas de tipos (expand type) e fusión automática de cláusulas fn :: (FR-COMP-005)
Os ADT suma e os rexistros produto existentes pódense estender con expand type UpperName = ...:
- Estender un ADT suma: engade novos construtores de variante ao tipo existente.
- Estender un rexistro produto: engade novos campos con nome (
field: Type) e xera automaticamente as súas funcións de acceso. - Variantes con nome de tipo sen nomes de construtor redundantes: nas declaracións suma de
typeeexpand type, unha variante cuxo nome é un tipo integrado sen máis (String,Int,Real/Float,Bool,Char), sen argumentos de tipo adicionais detrás, representa directamente valores dese tipo (sen escribirInt IntninString String). Quen chama pode pasar valores sen máis (describe "hej",describe 42) ou chamadas explícitas ao construtor (describe (String "hej"),describe (Int 42)), e as ecuacións poden coincidir tanto con literais ("admin",0) como con patróns tipados ((String s),(Int n)). - Fusión automática de cláusulas
fn ::/op ::(senexpand fn): volver declarar unfn ::ouop ::existente con novas cláusulas de patrón fusiona automaticamente esas cláusulas na definición existente da función, e coloca as cláusulas de patrón específicas de construtor ou de literal antes de calquera cláusula xeral de reserva (_/ 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
Espazos de nomes de módulo, acceso cualificado e visibilidade estrita das importacións por módulo (FR-COMP-012)
Todo ficheiro .rg ou paquete .rglib define un espazo de nomes de módulo (derivado do nome base do ficheiro ou dunha cabeceira explícita module :: <name>) e pode importar outros módulos mediante import <mod> ou import <mod> as <alias>.
- Módulos de varios ficheiros (
module :: <name>):
- Varios ficheiros que declaran a mesma cabeceira
module :: <name>(por exemplo,orm.rgeorm_changeset.rg, que comezan os dous conmodule :: orm) comparten un único espazo de nomes lóxicoorme ven directamente os tipos, construtores, campos de rexistro e funcións dos outros.
- Acceso cualificado (
mod.Type,mod.Constructor,mod.field,mod.fn):
- Os tipos exportados, os construtores de ADT suma, os accesores de campo de rexistros produto, as actualizacións de rexistros (
with) e as funcións pódense referenciar cun prefixo<mod_or_alias>.(http.Config,db.Status,http.Ok 200,http.port cfg,http.check status cfg).
- Resolución estrita de nomes sen cualificar por módulo, en 3 niveis:
- Cada módulo
M(incluído o módulo raíz da aplicación) resolve os tipos, construtores, campos de rexistro, funcións e chamadas de varias palabras sen cualificar estritamente segundo o grafo de importacións do propioM, en tres niveis de prioridade: - Nivel 1 — Módulo propio (
M): as declaracións do propioM(en todos os ficheiros que compartenmodule :: M) teñen sempre a prioridade máis alta. - Nivel 2 — Importacións directas de
M: seMnon define o nome,ridguncbusca nos módulos queMimporta directamente (import D). Se unha soa importación directaDdefine o nome, resólvese sen ambigüidade á declaración deD, aínda que outros módulos da unidade de compilación (dependencias transitivas, módulos irmáns ou a aplicación raíz) tamén definan o mesmo nome. Se dúas ou máis importacións directas deMdefinen o mesmo nome eMnon o define, as referencias sen cualificar recházanse por ambiguas (__ambiguous__.<name>) e deben cualificarse (d1.<name>fronte ad2.<name>). - Nivel 3 — Importacións transitivas de
M: só se consultan cando nin o Nivel 1 nin o Nivel 2 definen o nome. En consecuencia, unha importación directa enM(import json) oculta limpamente unha función auxiliar transitiva (codec.json_get_str, quesolsticeimporta internamente) sen obrigar asolsticenin aMa renomear ou cualificar as súas chamadas. - Illamento respecto de módulos irmáns non importados e da aplicación raíz: as declaracións definidas só en módulos que
Mnon importou (nin directa nin transitivamente), incluídas as bibliotecas irmás importadas pola aplicación raíz ou as declaracións da propia aplicación raíz ao compilar unha biblioteca importadaM, son invisibles para a busca sen cualificar dentro deM(__unimported__.<name>).
Gramática unificada de = ((none), |, ||) e variables locais (FR-COMP-003)
Toda definición ou ligazón en Ridgun, xa sexa unha cláusula de función (fn ::), unha cláusula de operador (op ::), unha variable local inmutable (x =), unha variable local mutable (mut x =) ou unha rama de garda (| cond = / | otherwise =), remata con = e acepta exactamente as mesmas catro formas de bloque ou expresión no lado dereito:
Prefixo despois de = | Semántica de execución | Significado |
|---|---|---|
<expr> en liña | Expresión única (con liñas opcionais || expr -> var debaixo) | Avalía <expr> directamente (resolvendo as ligazóns do DAG paralelo || que haxa debaixo). |
(none) (liñas sangradas) | Secuencial (de arriba abaixo) | Avalía as instrucións por orde (mut, asignacións, efectos secundarios); a última expresión é o resultado. |
| <cond> = ... | Condicional (gardas e árbores de decisión aniñadas) | Avalía as condicións booleanas de arriba abaixo ata a primeira rama True (ou | otherwise =). |
|| <expr> | Paralela (DAG ou tupla con green threads) | Avalía as ramas || concorrentemente (|| expr -> var para nodos do DAG con nome, || expr para resultados en tupla paralela). |
1. As gardas (|) sempre seguen a = (funcións, operadores, variables locais e árbores de decisión aniñadas)
Os bloques de gardas (| cond = ... e | otherwise = ...) sempre aparecen detrás dun =, o que unifica as cláusulas de función, as cláusulas de operador, as definicións de variables locais e as árbores condicionais aniñadas:
Cláusulas de función e de operador con gardas
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
Asignación de variables locais con gardas
Como | é válido despois de calquera =, as variables locais pódense ligar de forma condicional sen necesidade dunha palabra chave if/else nin dunha función auxiliar:
discount =
| is_vip = 20
| total > 100 = 10
| otherwise = 0
Bloques secuenciais de varias liñas, árbores de decisión profundas e peches aniñados (|. / ||.)
Cada rama de garda remata con =, así que o seu corpo pode ser á súa vez un bloque secuencial ((none)), un bloque paralelo (||) ou outro bloque de gardas | aniñado, o que permite árbores de decisión arbitrarias (de 3 ou 4 niveis, ou máis) dentro de | cond = e | otherwise =.
- Peche de niveis aniñados (
|.e||.): - Nos bloques simples dun só nivel ou ao final dunha función ou árbore, non fai falta ningún elemento de peche.
- Cando remata un subbloque
|(ou||) aniñado e a execución volve a un nivel exterior onde seguen máis ramas ou instrucións debaixo,|.(para|) ou||.(para||) pecha explicitamente ese nivel aniñado. - Son válidas tanto a colocación en liña ao final da última liña (
| otherwise = val |., o estilo compacto canónico) como a colocación na liña seguinte (|.):
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. Inmutables por defecto (x = ...) fronte a variables mutables explícitas (mut x = ...)
Dentro dos bloques secuenciais, as variables locais seguen a Opción 1:
- Ligazón inmutable (
x = <rhs>): - Declara unha nova variable local inmutable
x. - Tentar reasignar
x = ...máis adiante no mesmo ámbito sen unha declaración previamut x = ...é un erro de compilación. - Declaración mutable (
mut x = <rhs>) e reasignación (x = <new_rhs>): mut x = <rhs>declaraxcomo mutable no ámbito actual e inicialízaa.- As reasignacións posteriores de
xno mesmo ámbito escríbense simplementex = <new_rhs>(sen repetirmut). - Con
RMM2, reasignarx = <new_rhs>incrementa a nova estrutura (+1) antes de decrementar a antiga (-1, en fervenza cara aos seus fillos se o seu contador chega a0).
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 literais e construtores de 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
Primitivas de paralelismo (||)
Ridgun unifica toda a execución paralela baixo || (where elimínase):
Ligazóns con nome en DAG paralelo (|| 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
- Todas as liñas
|| expr -> varbaixo unha expresión forman un Grafo Acíclico Dirixido (DAG) paralelo. - O compilador analiza as dependencias de variables entre as liñas
||(xeynon teñen dependencias e execútanse en paralelo na Onda 1;zdepende dexeye execútase na Onda 2, antes de avaliarx + y + z). - Tamén se admiten escrituras paralelas en porcións de tensor (
|| expr -> out[i, j]).
Bloques de tubaxe paralela directa (|| expr)
op :: run Conf and Conf in parallel => Res, Res
run a and b in parallel =
|| run_process a
|| run_process b
Cada liña || execútase como un green thread paralelo separado, e os resultados devólvense como unha tupla na orde de declaración (ou agárdase por eles cando o bloque está dentro dun bloque secuencial).
Canles tipadas
Para fluxos de datos e topoloxías produtor-consumidor entre green threads, Ridgun ofrece canles MPMC tipadas (Channel):
| Función ou operador integrado | Sinatura | Finalidade |
|---|---|---|
new_channel | () => Channel | Crea unha canle tipada sen límite |
new_bounded_channel | Int => Channel | Crea unha canle acoutada cunha capacidade que aplica contrapresión |
send_through | T Channel => () | Envía un valor a unha canle (bloquéase se está acoutada e chea) |
receive_from | Channel => T | Recibe o seguinte valor dunha canle |
try_receive_from | Channel => Option | Consulta sen bloqueo que devolve Some v ou None |
Primitivas de E/S
Ridgun ofrece funcións integradas para a saída por terminal.
echo
Escribe na terminal un número arbitrario de cadeas, separadas por espazos e seguidas dun salto de liña.
fn :: echo String... => ()
O sufixo ... no tipo do último parámetro márcao como variádico: acepta un ou máis argumentos dese tipo.
echo "hello" -- prints: hello
echo "hello" "world" "!" -- prints: hello world !
echo "result: {result}" -- prints: result: 42
out
Un operador que escribe unha cadea directamente na terminal e a devolve (o que permite encadealo).
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
Resumo
| Función | Tipo | Finalidade |
|---|---|---|
echo | String... => () | Escribe cadeas con espazos e salto de liña |
out | String => String | Escribe unha cadea e devólvea para encadear |
Biblioteca estándar
As bibliotecas impórtanse con import:
import test
Probas unitarias (lib/test.rg)
Unha biblioteca de probas sinxela que usa echo para a saída por terminal.
Asercións:
| Operador | Finalidade |
|---|---|
assert Int equals Int | Igualdade de enteiros |
assert Real equals Real | Igualdade de reais |
assert String equals String | Igualdade de cadeas |
assert Bool is true | O booleano é verdadeiro |
assert Bool is false | O booleano é falso |
Estrutura das probas:
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
Saída na terminal:
═══ Arithmetic ═══
● sum adds correctly
✓ PASS
● factorial base case
✓ PASS
Resumo dos elementos sintácticos
| Elemento | Sintaxe | Finalidade |
|---|---|---|
| Comentario | -- text | Comentario dunha soa liña |
| Declaración de función | fn :: name types => return | Declara a sinatura de tipos dunha función |
| Definición de función | name params = body | Define a implementación dunha función |
| Declaración de operador | op :: words types words => return | Declara un operador en linguaxe natural |
| Definición de operador | words params words = body | Define a implementación dun operador |
| Varios retornos | => type, type | Declara varios valores de retorno |
| Parámetros variádicos | Type... | Acepta un ou máis argumentos dun tipo |
| Literal de tensor | Tensor [[1, 2], [3, 4]] | Crea un tensor N-dimensional |
| Indexación de tensor | t[0, 1] ou t[0][1] | Accede a elementos do tensor |
| Mutación de tensor | t[0, 1] = 5.0 | Modifica elementos do tensor in-place |
| Contracción de Einstein | A_ij @ B_jk | Contracción de tensores sobre índices compartidos |
| Operación de Hadamard | A_ij * B_ij | Aritmética elemento a elemento con aliñamento de índices |
echo | echo "a" "b" "c" | Escribe cadeas na terminal |
out | "text" out | Envía unha cadea á terminal |
| Ligazón local inmutable | x = expr | Liga unha variable local inmutable (= admite (none), |, ||) |
| Ligazón local mutable | mut x = expr | Declara unha variable local mutable (reasígnase con x = new_expr) |
| Bloque de gardas | | cond = expr | Rama de garda condicional despois de = (admite | aniñado) |
| Bloque de DAG paralelo | || expr -> var | Subcálculos paralelos con resultados do DAG con nome |
| Bloque de tubaxe paralela | || expr | Execución paralela que devolve unha tupla |
| Sobrecarga de operadores | op :: type + type => type | Sobrecarga operadores existentes para tipos novos |
| Literais de Bool | True, False | Valores primitivos booleanos |
| Operadores lóxicos | and, or, xor, not | Operadores lóxicos con palabras en minúsculas |
| Punto de entrada | fn :: main => () | Punto de entrada obrigatorio do programa |
Formato canónico do código fonte (stable format e ferramentas de editor)
Só estético, non é un requisito do compilador (
FR-TOOL-001): o analizador léxico e o sintáctico de Ridgun non dependen do aliñamento horizontal en columnas. Pararidgunc, os ficheiros fonte.rgsen aliñar e aliñados en columnas son 100 % equivalentes. As regras de abaixo definen a disposición estética canónica que aplicanstable formateridgun-modede GNU Emacs (C-c C-f).
- Grupo de aliñamento por sinatura (
fn ::/op ::):
- Cada declaración
fn ::ouop ::comeza un grupo de aliñamento que abrangue a súa sinatura, as súas cláusulas de pattern matching e as súas ligazóns|| expr -> var, e que remata na seguinte declaración de nivel superior (fn,op,type,import,global,context,shared,hardware,@).
- Aliñamento en columnas dos elementos do lado esquerdo ($C_0 = 6$):
fn ::eop ::teñen 6 caracteres de ancho, así que o elemento $0$ do lado esquerdo da sinatura comeza na columna $C_0 = 6$.- Cada posición de elemento $i \in [0, N-1]$ antes de
=>/->(tratando"...",'...',[...]e(...)como elementos únicos) ten un ancho $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))$ e comeza na columna $C_i = C_{i-1} + W_{i-1} + 1$. Os patróns de cláusula de máis de18caracteres trátanse como valores atípicos: non estiran $W_i$ para o resto do grupo e simplemente sobresaen na súa propia liña cun espazo. - As cláusulas de definición correspondentes sángranse ata a columna $6$ ($C_0$), de xeito que o nome da función ou as palabras do operador e cada patrón de argumento quedan aliñados en vertical baixo as súas posicións da sinatura.
- Aliñamento vertical de
=>,=e->($C_{\text{arrow}}$):
- Sexa $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)$ e $C_{\text{ret}} = C_{\text{arrow}} + \text{len}(\text{arrow}) + 1$.
- Na sinatura,
=>colócase en $C_{\text{arrow}}$ e o tipo de retorno en $C_{\text{ret}}$. - Nas cláusulas dunha soa liña (
<lhs> = <rhs>), nas cláusulas de bloque de varias liñas (<lhs> =) e nas cláusulas encabezadas por gardas (<lhs> =seguido de| cond = ...),=colócase en $C_{\text{arrow}}$ (aliñado xusto debaixo de=>); nas cláusulas dunha soa liña,<rhs>comeza en $C_{\text{ret}}$. - Nas ligazóns de DAG paralelo (
|| <lhs> -> <rhs>),||comeza na columna $8$ (6 + 2),->colócase en $C_{\text{arrow}}$ e<rhs>comeza en $C_{\text{ret}}$. - Alternativa compacta (
> 4parámetros ou> 80columnas): se unha declaración ten máis de4posicións de parámetro ou a súa liña de sinatura aliñada superaría os80caracteres, desactívase o recheo horizontal en columnas para ese grupo e os elementos sepáranse cun só espazo (" "), conservando o sangrado das cláusulas na columna 6.
- Sangrado estrutural (desprazamento de 2 espazos):
- Declaracións de nivel superior (
fn,op,type,import,global,context,shared,hardware,@): columna0. - Cláusulas de función ou de operador baixo unha sinatura: columna
6. - Gardas (
| cond = expr) e tubaxes paralelas (|| expr/|| expr -> var):prev_indent + 2(columna8dentro dunha cláusula, ouindent + 2dentro dunha ligazón local ou dunha garda aniñada). - Liñas do corpo dun bloque secuencial despois dunha liña que remata en
=:prev_indent + 2.
Operacións de orde superior sobre coleccións, secuenciais e paralelas (FR-COMP-001, FR-COMP-014)
Ridgun ofrece os combinadores secuenciais de coleccións habituais (map, filter, fold) xunto con combinadores paralelos multinúcleo explícitos co prefixo || (||map, ||filter). Os cinco combinadores admiten tanto a sintaxe idiomática de tubaxe ou infixa de esquerda a dereita (xs map f -> filter g -> fold 0 h -> res) como a sintaxe prefixa tradicional (map f xs):
| Integrado | Formas prefixa e de tubaxe | Modelo de execución |
|---|---|---|
map | xs map f / xs -> map f / map f xs | Secuencial: avalía f x estritamente de esquerda a dereita no fío que fai a chamada. |
| ` | map` | |
filter | xs filter p / xs -> filter p / filter p xs | Secuencial: avalía o predicado p x estritamente de esquerda a dereita no fío que fai a chamada. |
| ` | filter` | |
fold | xs fold init f / xs -> fold init f / fold f init xs | Pregado secuencial á esquerda: acumula acc = f acc x de esquerda a dereita, empezando desde init. |
E/S literal de ficheiros, fluxos de bytes e funcións integradas POSIX (FR-VM-001, FR-VM-002)
- E/S literal de ficheiros e fluxos de bytes (
FR-VM-001):
read_file : String => Stringle o contido dos ficheiros tal cal (conserva\r\ne a presenza ou ausencia exacta do salto de liña ao final do ficheiro, e nunca executa o preprocesador deimportde.rgdo compilador sobre ficheiros de datos).load_rg_source : String => Stringúsaa exclusivamente a cadea de ferramentas do compilador ao cargar ficheiros fonte.rgcon inserción deimport.read_bytes : String => [Int]ewrite_bytes : String [Int] => ()len e escriben fluxos de bytes binarios en cru (0..255, incluídos0x00NUL e0x80..0xFF).
- Funcións integradas POSIX de sistema de ficheiros, procesos e seguridade (
FR-VM-002):
- Sistema de ficheiros:
list_dir,file_lstat,sha256_file,sys_mkdir,sys_chmod. - Procesos e contorno:
sys_exec,sys_chdir,sys_getcwd,sys_getenv,sys_setenv,sys_exit. - Seguridade e separación de privilexios:
sys_getuid,sys_getgid,sys_username,sys_user_groups,sys_lookup_uid_gid,sys_setresgid,sys_setgroups,sys_setresuid,sys_execve,sys_read_pass.
- E/S da PAL multiplataforma, sondaxe, primitivas de procesos, ADT de destino e cotas declarativas
with timeout(FR-COMP-007):
- ADT predeclarados da plataforma de destino:
type SysOs = Linux | MacOS | Windows(sys os => SysOs, aberto a extensións medianteexpand type SysOs = ...)type SysArch = X86_64 | Arm64(sys arch => SysArch, aberto a extensións medianteexpand type SysArch = ...)- Literais con unidade de duración: os literais enteiros aceptan os sufixos
_ms(* 1),_s(* 1000) e_min(* 60000) (por exemplo,500_ms,5_s,2_min), que complementan os sufixos de unidade de bytes (_KB,_MB,_GB). - Capa 1 — Primitivas PAL multiplataforma para as bibliotecas do sistema
.rglib(posix.rglib/io.rglib): sys os => SysOsesys arch => SysArch(ADT suma do sistema operativo e da arquitectura de destino en tempo de compilación)sys open mode String String => Int(path, modo portable"r","w","a","rw","w+","a+"-> descritor ou handle>= 0ou-errno, traducido segundo o sistema operativo de destino)sys open String Int Int => Int(path,flagsnormalizados da PAL de Ridgun (0= lectura,1= escritura,2= lectura e escritura,64= crear,512= truncar,1024= engadir,2048= non bloqueante),mode->fd >= 0ou-errno)sys close Int => Int(fd->0ou-errno)sys read fd Int Int => String(fd,max_bytes-> cadea lida defd)sys write fd Int String => Int(fd,data-> bytes escritos ou-errno)sys poll Int Int Int => Int(fd,eventsnormalizados da PAL de Ridgun (1=POLLIN/lectura,4=POLLOUT/escritura),timeout_ms->> 0revents cando está listo,0se se esgota o tempo,< 0con-errno; usasys remaining mscandotimeout_ms < 0dentro dewith timeout)sys terminate Int Bool => Int(pid,force-> terminación de procesos portable, ordenada ou forzada) esys kill Int Int => Int(pid, sinal normalizado ->0ou-errno)sys call Int Int Int Int Int Int Int => Int(nr,a1..a6-> vía de escape de baixo nivel para chamadas ao sistema segundo a ABI do sistema operativo de destino)sys time ms => Intesys sleep ms Int => ()(reloxo monótono en milisegundos e espera)sys deadline ms => Intesys remaining ms => Int(prazo léxico activo do tempo límite e milisegundos que quedan, ou-1se non hai límite)- Capa 2 — Cota de execución declarativa
with timeout <duration> do: - Igual que
with region 64_KB doestablece unha cota léxica de bytes,with timeout 500_ms do(ou a forma combinadawith region 64_KB, timeout 500_ms do) establece un prazo léxico de execución (sys_time_ms + duration_ms) no contexto do fío actual e restaura o prazo anterior ao saír do bloque:
with timeout 500_ms do
rem = sys remaining ms
rc = sys poll fd 1 rem
rc
- Consulta
docs/lang/pal-and-system-libraries.mdpara ver a especificación completa da arquitectura da PAL e das bibliotecas do sistema (.rglib).
- Compilación de bibliotecas reutilizables (
.rglibe ELF64.so) estableextensible (FR-COMP-006,FR-TOOL-003):
ridgunc build --lib <file.rg> -o <out.rglib>eridgunc build --so <file.rg> -o <lib.so>(oustable built lib/stable new --lib <name>) compilan módulos sen necesidade defn :: main => ()e conservan todas as funcións, os operadores e os tipos exportados.stabledescobre comandos propiosfn :: task <words...> => Stringe extensiónsfn :: plugins => [String](plugins/*.rg,.stable/plugins/*.rg), que se listan constable tasksestable plugins.
Ridgun Memory Management (RMM): RMM1 (CMM) e RMM2 (FR-COMP-002)
Ridgun xestiona a memoria do heap (String, [a], (a, b), ADT, Tensor) de forma determinista, sen un colector de lixo (GC) de rastrexo, mediante Ridgun Memory Management (RMM):
- Política
RMM1— Xestión de Memoria Compilada (CMM lineal + condicional):
- Aplícase cando o fluxo de control e as funcións chamadas se coñecen estaticamente en tempo de compilación, sobre ligazóns non aniñadas:
- CMM lineal (
rg_cmm_move/rg_cmm_drop): o compilador determina en tempo de compilación a instrución exacta do último uso de cada variable local, move as variables devoltas (rg_cmm_move(&mut lv_x)) e emite unha liberación incondicional (rg_cmm_drop(&mut lv_x)) xusto despois do último uso dunha variable cando non queda vivo ningún posible alias. - CMM condicional (
rg_cmm_drop_unless/ptr(a) != ptr(c)): cando unha chamada con ramificación estática (c = choose_list cond a b) devolve unha de varias entradas candidatas e unha ou máis candidatas seguen vivas xunto conc, o compilador constrúe a táboa de devanceiros con posibles alias (RCmmAlias) e emite unha garda de identidade de punteiros de 1 ciclo na instrución do último uso estático 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)
- Política
RMM2— Conta por asignación e fervenza estrutural (política activa):
- Unifica a xestión de memoria entre funcións, retornos con ramificación (
choose_list), contedores aniñados (xs = [a, b, ..., z],xs[3] = a), variables locais mutables (mut x = ...) e execución en varios fíos: - Escalares primitivos (
Int,Real,Bool,()): nunca se contan; cópianse por valor na pila ou nos rexistros do procesador. - Argumentos de entrada da función (
pats): nunca se tocan (+0ao entrar,-0ao saír). - Variables locais internas (
x = .../mut x = ...): reciben+1na asignación (x = expr,xs[3] = a). Na reasignación dunha variable mutable (x = new_expr),new_exprrecibe+1antes de que o antigoxreciba-1. - Fin da función ou saída do ámbito:
- As variables locais internas que non se devolven reciben primeiro
-1: serc == 0, libérase o bloque pai e propágase-1en fervenza, un por un, a cada estrutura filla directa (liberando recursivamente calquera filla cuxorcchegue a0). - Unha variable local interna devolta (
return a) recibe-1(ou móvese conrg_cmm_move), e aínda que o seurcsexa0en tránsito, exímese para que a asignación de quen chama (c = make_pair ()) lle sume+1(0 + 1 = 1). Cando se devolve un argumento de entrada (choose_list cond a b), non se tocou dentro da función (rc = 1), así que oc = choose_list cond a bde quen chama súmalle+1(1 + 1 = 2, compartido entreaec).
- Política
RMM3(reservada):
- Reservada para unha futura política de optimización estática agresiva.
- Consulta
docs/etc/cmm_architecture.md(no repositorio de Ridgun) para ver a especificación completa deRMM1eRMM2.