Aprenda Fazendo
25 Exemplos Progressivos
Do Hello World a Microkernels — cada exemplo ensina um conceito novo.
O ponto de partida. Em Artcode, println é uma função builtin que aceita
qualquer valor e imprime com quebra de linha. Não há boilerplate — nenhuma função
main obrigatória para scripts simples.
// Exemplo 00 - Olá Mundo println("Hello, Artcode!");
Artcode usa let para bindings imutáveis e var para mutáveis.
A inferência de tipos é automática — Int, Float,
String e Bool são detectados pelo compilador sem anotação.
// Exemplo 01 - Valores e Variáveis let inteiro = 42; let flutuante = 3.14; let texto = "Artcode"; let booleano = true; println(inteiro); println(flutuante); println(texto); println(booleano); let soma = inteiro + 8; println(soma);
Arrays em Artcode são dinâmicos e heterogêneos por padrão. O tipo
Optional é expresso com none — um literal especial que
garante ausência de valor sem recorrer a null ou ponteiros nulos.
// Exemplo 02 - Arrays e Option (None) let nums = [1, 2, 3]; let vazio = []; let opt = none; // literal option vazio println(nums); println(vazio); println(opt);
Artcode usa if/else clássico, mas os operadores lógicos
são palavras-chave: and, or, !.
Escopos aninhados com { } criam novos namespaces — variáveis locais
não "vazam" para o escopo externo (shadowing controlado).
// Exemplo 03 - Controle de Fluxo let a = 10; let b = 20; if (a < b) { println("a < b"); } else { println("a >= b"); } if (a < b and b > 15) { println("duas condições"); } let sol = true; if (sol) { println("Praia!"); } if (!sol or a == 10) { println("Uma condição verdadeira"); } // Escopo aninhado let nivel = "global"; { let nivel = "local"; println(nivel); } println(nivel); // "global" — não foi afetado
Funções são declaradas com func e suportam anotações de tipo opcionais
nos parâmetros e retorno. Closures capturam o ambiente léxico automaticamente —
variáveis do escopo externo ficam acessíveis sem nenhuma sintaxe especial.
// Exemplo 04 - Funções e Captura de Escopo func soma(a: Int, b: Int) -> Int { return a + b } println(soma(2, 3)); let x = 5; func inc(n) { return n + x } // captura `x` do escopo externo println(inc(10)); // 15
struct define tipos produto com campos nomeados.
enum define tipos soma com variantes que podem carregar dados.
match faz pattern matching exaustivo — casos com .Variant
desestrutura os dados da variante automaticamente.
// Exemplo 05 - Structs, Enums e Match struct Pessoa { nome: String, idade: Int } enum Status { Ok, Erro(String) } let p = Pessoa { nome: "Ada", idade: 42 }; println(p.nome); println(p.idade); let falha = Status.Erro("Falhou"); match falha { case .Ok: println("Tudo certo") case .Erro(msg): println(msg) }
Variantes de enum podem ser referenciadas sem o prefixo do tipo quando o compilador
consegue inferir o contexto — use .Variant em vez de
Tipo.Variant. Para evitar ambiguidade, a forma qualificada
(Resultado.Ok(10)) é sempre válida.
// Exemplo 06 - Enum Shorthand & Inferência enum Resultado { Ok(Int), Err(String) } let r1 = Resultado.Ok(10); let r2 = Resultado.Err("Falha"); match r1 { case .Ok(v): println(v) case .Err(e): println(e) } match r2 { case .Ok(v): println(v) case .Err(e): println(e) }
Cases de match aceitam um guard opcional com if para adicionar
predicados extras sobre o valor desestruturado. Permite lógica condicional
precisa sem aninhamento de ifs dentro dos arms.
// Exemplo 07 - Pattern Matching com Guards enum E { V(Int), W(Int) } let x = E.V(10); let y = E.V(5); let z = E.W(8); match x { case .V(v) if v > 8: println(f"v>8: {v}") case .V(v): println(f"v: {v}") case .W(w): println(f"w: {w}") }
f-strings em Artcode suportam especificadores de formato embutidos com
: — como {n:hex}, {s:upper},
{s:trim}, {s:pad10}. Chaves literais são escapadas
com {{ }}.
// Exemplo 08 - f-Strings e Especificadores de Formato let n = 255; let s = " AbC "; println(f"hex={n:hex} trim={s:trim} upper={s:upper}"); println(f"lower={s:lower} pad={s:pad10}"); println(f"chave literal: {{ {n} }}");
Métodos são funções declaradas com func Tipo.nome(self). O
self é auto-bound ao receiver na chamada. Enums têm acesso a
variant e values intrinsecamente para inspeção em runtime.
// Exemplo 09 - Métodos em Structs e Enums struct Pessoa { nome: String } func Pessoa.greet(self) { println(f"Olá, {self.nome}!"); } let p = Pessoa { nome: "Ada" }; p.greet(); // → Olá, Ada! enum Status { Ok, Err(String) } func Status.describe(self) { println(f"variant={variant} values={len(values)}"); } let s1 = Status.Ok; let s2 = Status.Err("x"); s1.describe(); s2.describe();
O enum Result (Ok, Err) está no prelude e é
usado para propagação explícita de erros. Sem exceções implícitas — cada falha
é um valor que o caller deve tratar com match.
// Exemplo 10 - Propagação explícita de Result func divide(a: Int, b: Int) -> Result { if b == 0 { return Result.Err("/0") } return Result.Ok(a / b) } func quarter(n: Int) -> Result { let r = divide(n, 2); match r { case .Ok(h): { let r2 = divide(h, 2); match r2 { case .Ok(q): return Result.Ok(q) case .Err(e): return Result.Err(e) } } case .Err(e): return Result.Err(e) } } let r = quarter(40); match r { case .Ok(v): println(f"q={v}") case .Err(e): println(e) }
Arrays têm métodos nativos como .sum(), .count(),
.map(fn) e .filter(fn). O type-checker emite
diagnósticos se você chamar sum() em um array heterogêneo.
// Exemplo 11 - Métodos builtin de Array let nums = [1, 2, 3, 4]; println(nums.sum()); // 10 println(nums.count()); // 4 let mixed = [1, 2, "a"]; println(mixed.count()); // 3 (ok — heterogêneo) // mixed.sum() → erro de tipo (array misto)
O runtime Artcode emite métricas de execução no stderr ao finalizar:
contagem de erros tratados, crash_free%, chamadas de função etc.
Este exemplo mostra como evitar divisão por zero e ainda observar as métricas.
// Exemplo 12 - Métricas de Execução (ver stderr) println("A"); println("B"); let denom = 0; if denom == 0 { println("denom is zero, skipping division") } else { println(10 / denom) } println("C"); // → stderr: handled_errors=0 crash_free=100%
weak cria uma referência fraca — ela não impede o GC(ARC) de liberar
o objeto. O operador w? é o upgrade opcional: retorna o
valor se o dono ainda existir, ou None se já foi dropado.
// weak cria referência fraca para `a` let a = 1; let w = weak a; // w? — upgrade opcional: retorna o valor se `a` existir let opt = w?;
Stub mínimo usado pelo linter de dependências para detectar ciclos de referência
potenciais. Cria uma referência weak "morta" — útil para verificar
que o linter identifica o padrão sem precisar de um ciclo real.
// Nenhum ciclo real — weak "morta" simulada let a = 1; let w = weak a; // sem API para drop programático ainda
on_finalize(obj, fn) registra uma função que será chamada quando o
GC(ARC) liberar obj. Finalizers rodam de forma assíncrona e podem
promover handles externos. Blocos performant usam a arena interna.
let outside = [99]; func fin_promote() { let _promoted = outside; } // Registra finalizer dentro de um bloco performant performant { let _target = [1, 2, 3]; on_finalize(_target, fin_promote); } // Ao sair do bloco, _target é finalizado e fin_promote é chamado
weak(obj) retorna um handle que não mantém o objeto vivo.
unowned(obj) é um ponteiro direto sem ownership — válido enquanto
o dono existir. weak_get(w) e unowned_get(u) são as
APIs de depuração para obter o valor.
// Exemplo 1: weak — upgrade falha após drop do strong { let owner = [99]; let w = weak(owner); println("inside:", weak_get(w)); } println("after block: owner foi dropado"); // Exemplo 2: unowned — válido enquanto dono existir { let owner2 = [7]; let u = unowned(owner2); println("inside:", unowned_get(u)); } println("after block: owner2 foi dropado");
Blocos performant alocam objetos em uma arena de curta duração —
o GC processa tudo de uma vez ao sair do bloco. Combinando com
on_finalize, é possível observar e reagir ao ciclo de vida de
objetos de curta duração com precisão.
let outside = [99]; let promoted = none; func fin_promote() { let promoted = outside; } performant { let _a = [1, 2, 3]; on_finalize(_a, fin_promote); } // após блок: _a finalizado, fin_promote chamado
A stdlib embutida inclui: Map (map_new, map_set,
map_get), Set, funções matemáticas (math_abs,
math_pow, math_clamp), time_now,
rand_next e I/O de arquivos (io_write_text,
io_read_text).
// Map let m = map_new(); map_set(m, "hello", 42); println(map_get(m, "hello")); // 42 println(len(m)); // 1 // Set let s = set_new(); set_add(s, 10); set_add(s, 10); // duplicata ignorada println(len(s)); // 1 // Math println(math_pow(2, 3)); // 8 println(math_clamp(15, 1, 10)); // 10 // IO io_write_text("out.txt", "Hello IO!"); println(io_read_text("out.txt"));
Blocos performant { } são uma funcionalidade experimental que
contorna o GC padrão usando uma arena de memória. Objectos alocados dentro
não podem vazar para fora — o type-checker rejeita return
de valores da arena.
performant { let x = [1, 2, 3]; // return x; ← rejeitado pelo TypeInfer (não pode vazar arena) }
Artcode tem um modelo de atores baseado em mensagens. spawn actor { }
cria um ator e retorna seu ID. actor_send(id, envelope(...)) envia
mensagens. run_actors() aciona o scheduler cooperativo para processar a fila.
// Spawn do ator como expressão — retorna actor id let a = spawn actor { let msg = actor_receive(); if msg { println(msg); } }; // Enviar envelopes para o ator `a` let ok1 = actor_send(a, envelope(none, 7, 1)); let ok2 = actor_send(a, make_envelope(42, 5)); // Drive the scheduler — ator processa as mensagens run_actors();
Demonstra uma carga CPU-bound com recursão para gerar chamadas quentes de função —
útil para benchmarks de JIT/interpretador. A função sum_work usa
tail-recursion e é chamada repetidamente para simular um microkernel de cálculo.
func sum_work(n) { func sum_down(i, acc) { if i <= 0 { return acc; } return sum_down(i - 1, acc + i); } return sum_down(n, 0); } func main() { // 200 chamadas de sum_work(20) para gerar hot calls sum_work(20); sum_work(20); sum_work(20); // ... (200 total) return 0; } main();
Código intencionalmente mal-formatado para testar o auto-formatter
art fmt. Espaçamento inconsistente, chaves sem espaço —
o formatter deve normalizar tudo enquanto preserva comentários e lógica.
// This file has terrible formatting! let x= 10; if x >5{ println("Bigger!"); // I want to keep this comment intact! if x>20 { println("Huge!"); } else { println("Moderate"); } } // art fmt → normaliza espaçamento automaticamente
Demonstra dois padrões que o linter art lint detecta:
shadowing suspeito (reuso de nome de variável em escopo aninhado)
e arms mortos em match (case após wildcard _
nunca será executado).
func test_shadowing() { let x = 10; if x > 5 { let x = 20; // ← linter: suspicious shadow println(x); } } func test_dead_arms() { let value = 5; match value { case _: println("Caught all!"); case 10: println("unreachable"); // ← dead code } } test_shadowing(); test_dead_arms();
Açúcar sintático avançado para monads nativas: if let Ok(v) = expr
desestrutura inline; .is_ok(), .is_err(),
.unwrap() e .unwrap_or(default) são métodos builtin de
Result e Option.
func test_result_sugar() { let success = Result.Ok(42); let failure = Result.Err("Went wrong"); println(success.is_ok()); // true println(success.unwrap()); // 42 println(failure.unwrap_or(-1)); // -1 if let Ok(value) = success { println("Success:", value) } if let Err(msg) = failure { println("Failure:", msg) } } func test_option_sugar() { let some_val = Option.Some("Hello"); let none_val = Option.None; println(some_val.is_some()); // true println(none_val.unwrap_or("Default")); // "Default" if let Some(text) = some_val { println(text) } } test_result_sugar(); test_option_sugar();