Makoto × TypeScript
Mapeamento conceito a conceito da linguagem Makoto para TypeScript. Gerado de
docs/mappings/_gen/em 2026-10-05. Fonte de verdade do design:docs/english/*.md; sintaxe Makoto dos exemplos marcados verificada no compiladormk. 173 conceitos — implementado: 146 · parcial: 7 · planejado: 10 · não existe (por design): 10
Legenda: implementado · parcial · planejado · não existe (por design)
Outras comparações: C · JavaScript · Java · Rust · Stdlib · Índice
Sumário
Section titled “Sumário”- Filosofia e modelo mental
- Declarações e tipos básicos
- Controle de fluxo
- Funções, lambdas, métodos e despacho
- Tipos: struct, enum, interface, union
- Coleções e Optional
- Strings e texto
- Erros: Result, conjuntos de erro, catch
- Memória: gerenciamento sem cor, ponteiros, ownership
- Concorrência: processos e canais
- Supervisão e resiliência (let it crash)
- Canais pela rede (
net) - Generics, comptime e reflexão
- Geradores (sequências preguiçosas)
- Contratos (DbC), assert/assume e unsafe
- FFI com C (e C++)
- Assembly inline
- Módulos, visibilidade e pacotes
- Observabilidade do runtime
- Testes, benchmarks, property-based e fuzzing
- Extensão UI (
.mkoui) - Camada de verificação (planejada)
- Ferramentas (
mk)
Filosofia e modelo mental
Section titled “Filosofia e modelo mental”Antes da sintaxe: o que a Makoto (誠, “sinceridade”) recusa de propósito. Quem vem de C/JS/TS/Java/Rust
tropeça mais nestes pontos do que em qualquer palavra-chave. Regra geral: o caso comum e seguro não leva
marcação; só capacidade extra ou perigo é marcado (var, mut, unsafe, @mm(none)…). Cada sistema da
linguagem (memória, concorrência, execução, tipos, FFI) carrega só as suas “cores” e não vaza pros outros.
Sem null / undefined / nil
Section titled “Sem null / undefined / nil”Status: não existe (por design)
Não existe null. Ausência é tipada: Optional[T] com some(v) / none. Um ponteiro *T nunca é nulo;
ponteiro que pode faltar é Optional[*T] (inclusive o NULL que vem do C).
Makoto
fn find(xs: []int, t: int) -> Optional[int] { loop i in 0..xs.len { if xs[i] == t { return some(i) } } return none}TypeScript
function find(xs: number[], t: number): number | undefined { const i = xs.indexOf(t); return i === -1 ? undefined : i;}Diferença:
T | undefinedcomstrictNullChecksé o mais próximo.Optional[T]é um enum de verdade (some/none).
Sem exceções (erro é valor)
Section titled “Sem exceções (erro é valor)”Status: não existe (por design)
Não há throw/try/catch de exceção. Falha é valor: Result[T, E] ou um conjunto de erros error{...}.
Propagar é explícito (catch |e| { return e }) — não existe o operador ?. Falha irrecuperável é
runtime.panic(...), que mata só o processo atual (o supervisor decide o que fazer).
Makoto
fn parse(s: string) -> Result[int, error{Empty}] { if s == "" { return Err(error.Empty) } return Ok(42)}v := parse(s) catch |e| { return e } // propaga explicitamenteTypeScript
function parse(s: string): number { // o tipo não diz que lança if (s === "") throw new Error("Empty"); return 42;}Diferença: TS não tipa exceções. Padrão mais próximo: retornar
{ ok: true, value } | { ok: false, error }.
Sem classes nem herança
Section titled “Sem classes nem herança”Status: não existe (por design)
Não há class, extends, this implícito nem herança. Dados ficam em decl (struct); comportamento em
funções com receiver explícito (fn (p: Point) norm()); reuso por composição (@embeds) e
interfaces com satisfação estrutural (estilo Go).
Makoto
decl Point { x: int; y: int }fn (p: Point) sum() -> int { return p.x + p.y }p := Point{x: 1; y: 2}io.println(p.sum())TypeScript
class Point { constructor(public x: number, public y: number) {} sum(): number { return this.x + this.y; }}Diferença: Pense em
interface+ funções livres, não em classe.
Sem async/await (IO sem cor)
Section titled “Sem async/await (IO sem cor)”Status: não existe (por design)
Não existe async, await, Future nem Promise. IO parece síncrono; o scheduler suspende só o
processo que bloqueou. Paralelismo de IO = vários processos (spawn) + canais. Nenhuma função é “colorida”.
Makoto
fn fetch(ch: Channel[int]) -> int { v := -> ch catch |e| { return 0 } // bloqueia só ESTE processo; os outros seguem return v}TypeScript
async function handle(path: string): Promise<string> { return fs.promises.readFile(path, "utf8");}Diferença: Sem
Promise<T>: o tipo de retorno é o valor mesmo (ouResult).
Sem sobrecarga de operadores/funções
Section titled “Sem sobrecarga de operadores/funções”Status: não existe (por design)
Tipo do usuário não sobrecarrega + - * / (use método nomeado: a.add(b)). Duas funções soltas com o mesmo
nome = erro “defined twice”. “Mesmo nome, tipos diferentes” é despacho explícito: fn name = { f1, f2 }
(function set) ou match T dentro de um genérico. Mesmo nome com aridade diferente = parâmetro default ou variádico.
Makoto
fn int_s(i: int) -> string { return "int" }fn bool_s(b: bool) -> string { return b ? "true" : "false" }fn to_s = { int_s, bool_s } // roteador por tipo, resolvido em compile-timeTypeScript
function toS(x: number): string;function toS(x: boolean): string;function toS(x: any) { return String(x); }Diferença: Overloads de TS são só assinaturas sobre um corpo único.
Sem retorno implícito
Section titled “Sem retorno implícito”Status: não existe (por design)
Bloco não vale a última expressão. Para produzir valor ou sair: return, break ou panic. Exceções de
glifo, que também são marcadores explícitos: o => do lambda de uma expressão e o ternário ?:. O => de um
braço de match não entrega valor — é separador.
Makoto
fn double(x: int) -> int { return x * 2 } // `x * 2` sozinho não compilav := match n { 0 => return "zero"; _ => return "outro" }TypeScript
const dbl = (x: number): number => x * 2;Sem zero-value / undefined implícito
Section titled “Sem zero-value / undefined implícito”Status: não existe (por design)
Todo binding nasce com um valor escrito. Não há int x; com lixo, nem zero-value do Go, nem undefined.
0, "" e List.new() só existem quando você escreve.
Makoto
var total := 0 // precisa do RHSvar xs: List[int] = List.new()TypeScript
let total: number; // erro só se lido antes de atribuirDeclarações e tipos básicos
Section titled “Declarações e tipos básicos”Imutável é o padrão. := declara (tipo inferido), : T = declara com tipo, = sozinho só atribui (e só a
var). Inteiros têm qualquer largura de 1 a 65535 bits e estouro dá trap; floats são sempre finitos
(NaN/overflow = erro).
Binding imutável: := / let
Section titled “Binding imutável: := / let”Status: implementado
x := v (atalho) ou let x := v: imutável em runtime. Tipo explícito estilo Odin: x: int = 5. Redeclarar o
mesmo nome no mesmo escopo é erro (não há shadowing local; parâmetro pode ser sombreado no corpo).
Makoto
x := 5let y: int = 10x = 6 // ERRO: x não é varTypeScript
const x: number = 5;Binding mutável: var
Section titled “Binding mutável: var”Status: implementado
var x := v é mutável. Um var que nunca é mutado é erro de compilação (use :=). Mutabilidade é do
lugar (o binding), não do campo: p.x = 1 só vale se p for var.
Makoto
var n := 0n = n + 1n += 2TypeScript
let n: number = 0; n += 3;Constante de compile-time: const
Section titled “Constante de compile-time: const”Status: implementado
const X := expr exige RHS conhecido em compile-time (pode chamar comptime fn). É o atalho de comptime let.
comptime var é o “rascunho” mutável que só existe durante a compilação.
Makoto
const SIZE := 64comptime fn fact(n: int) -> int { return n <= 1 ? 1 : n * fact(n - 1) }const F := fact(5) // 120, calculado pelo compiladorTypeScript
const SIZE = 64 as const;Diferença:
as constestreita o tipo literal, mas o valor é de runtime.
Descartar valor: _ :=
Section titled “Descartar valor: _ :=”Status: implementado
Largar um valor de retorno no chão sem _ := é erro: descartar é uma intenção e intenção se escreve.
Makoto
_ := compute()TypeScript
void compute();Múltiplos bindings, tuplas e swap
Section titled “Múltiplos bindings, tuplas e swap”Status: implementado
Tupla (a, b) agrupa valores. Desestruturar: q, r := f(). Troca: a, b = b, a. Função com vários retornos
sem erro devolve tupla.
Makoto
fn divmod(a: int, b: int) -> (int, int) { return (a / b, a % b) }q, r := divmod(7, 2)var a := 1var b := 2a, b = b, aTypeScript
function divmod(a: number, b: number): [number, number] { return [Math.trunc(a/b), a % b]; }Inteiros de largura arbitrária iN/uN
Section titled “Inteiros de largura arbitrária iN/uN”Status: implementado
Família paramétrica: i3, u7, u777, i65535… Comuns: i8..i128, u8..u128, isize/usize,
int/uint (largura padrão), byte = u8, bit = u1. Aritmética checada contra a largura declarada.
Makoto
small: u3 = 7b: byte = 255big: u128 = 1TypeScript
const b: number = 255;const big: bigint = 1n;Estouro dá trap; +% -% *% embrulham
Section titled “Estouro dá trap; +% -% *% embrulham”Status: implementado
+ - * fazem panic se o resultado sai da faixa (constante que estoura = erro de compilação). Aritmética
modular é pedida no operador: +%, -%, *%. Shift <</>> mascara pra largura (não dá trap).
Makoto
x: u8 = 250y := x +% 10 // 4 (embrulha)z := x + 10 // PANIC em runtime: estouro de u8TypeScript
(250 + 10) & 0xffConversão explícita com as
Section titled “Conversão explícita com as”Status: implementado
Conversões que perdem informação (largura, floatint) são escritas com as. as é só de valor (nunca de
ponteiro — pra ponteiro há mem.reinterpret, planejado).
Makoto
z := 300 as u8 // 44i := 3.9 as i32 // 3TypeScript
const z = 300 & 0xff;Floats IEEE finitos (NaN/inf = erro)
Section titled “Floats IEEE finitos (NaN/inf = erro)”Status: implementado
f16/f32/f64/f128/f256 + bf16; float = padrão. Uma operação que geraria NaN (0.0/0.0, √negativo) ou
estouraria pra inf é erro de runtime — todo float é finito e totalmente ordenado (< é bool puro).
Infinito direcionado de verdade é opt-in: ExtendedFloat.
Makoto
h: f16 = 1.5q: f128 = 1.0z := 0.0 / 0.0 // ERRO em runtime (NaN proibido)TypeScript
const z: number = 0 / 0;Precisão arbitrária: BigInt, Decimal, BigFloat, BigFloatArb
Section titled “Precisão arbitrária: BigInt, Decimal, BigFloat, BigFloatArb”Status: implementado
Core-blessed (usam + - * /), mas vivem fora do escopo global. BigInt cresce sem estourar; Decimal/
Decimal[P, S] base-10 exato (dinheiro); BigFloat[M] com M bits de mantissa (MPFR); BigFloatArb[M] com
cota de erro rigorosa (sem <, usa .cmp() ArbOrdering com Indeterminate). Literal toma o tipo do binding.
Makoto
a: Decimal = 0.10b: Decimal = 0.20io.println(a + b) // 0.30 exatox: BigInt = 123456789012345678901234567890io.println(x * x)TypeScript
const x: bigint = 123456789012345678901234567890n;Caracteres: byte, codepoint, grapheme (char)
Section titled “Caracteres: byte, codepoint, grapheme (char)”Status: implementado
Literal 'w' não tem tipo fixo: vira byte, codepoint (u32) ou grapheme pelo contexto. char é alias de
grapheme (o caractere visual, pode ter vários codepoints). Sem contexto, qualifique: 'w'.codepoint.
Makoto
k: codepoint = 'w'b: byte = 'w'g: char = 'é'TypeScript
const k: number = 'w'.codePointAt(0)!;Literais de duração (5s, 300ms)
Section titled “Literais de duração (5s, 300ms)”Status: implementado
Duration é builtin (u64 nanossegundos, nunca negativo). Unidades no lexer, sem import: ns ms s m h d.
Usado em timeout(...) e @supervisor(window: 10s). timeout(_) = sem prazo explícito.
Makoto
t := 300msreply := ch <-> req timeout(5s) catch |e| { return }TypeScript
const t: number = 300;Literais de tamanho (16mb, 4kib)
Section titled “Literais de tamanho (16mb, 4kib)”Status: implementado
Notação de lexer convertida pra usize em bytes: 512b, 4kb/4kib, 16mb/16mib, 2gb/2gib,
8tb/8tib (base-10 vs base-2). Usado em @limit(16mb).
Makoto
n := 4kib // 4096@limit(16mb)spawn worker(data)TypeScript
const n = 4 * 1024;Alias de tipo: alias
Section titled “Alias de tipo: alias”Status: implementado
alias UUID = u8 é sinônimo transparente (intercambiável). Tipo distinto é um decl envolvendo o valor.
Makoto
alias UUID = u8u: UUID = 7io.println(u + 1)TypeScript
type UUID = number;Comparação e Ordering
Section titled “Comparação e Ordering”Status: implementado
Tipos numéricos/strings têm ordem total (< > <= >= == != retornam bool). Ordering { Less; Equal; Greater }
é builtin: resultado de .cmp(). Um tipo do usuário vira ordenável implementando cmp() -> Ordering
(interface Comparable).
Makoto
o := 5.cmp(3) // GreaterTypeScript
const o: -1 | 0 | 1 = Math.sign(a - b) as -1 | 0 | 1;Unit, Never e o Top ausente
Section titled “Unit, Never e o Top ausente”Status: implementado
decl Marker {} é o struct vazio (Unit: um único valor). decl Never { _ } é o enum sem variantes (inabitado).
O “Top” (Any, interface{}, Object) não é escrevível: use um bound de interface.
noreturn é o tipo de retorno de quem nunca volta (panic, loop infinito).
Makoto
decl Marker {}decl Never { _ }fn die(msg: string) -> noreturn { runtime.panic(msg) }TypeScript
type Never = never;function die(m: string): never { throw new Error(m); }Controle de fluxo
Section titled “Controle de fluxo”Um loop para todos os laços, um match para switch/match/select, if é só statement (valor por condição
= ternário). loop e match podem ser valor, entregando por return explícito.
if / else (statement)
Section titled “if / else (statement)”Status: implementado
if não produz valor. Condição é bool estrito (sem truthiness).
Makoto
if n > 0 { io.println("pos") } else if n < 0 { io.println("neg") } else { io.println("zero") }TypeScript
if (n > 0) {...} else if (n < 0) {...} else {...}Ternário c ? a : b
Section titled “Ternário c ? a : b”Status: implementado
Escolha entre dois valores. Três ou mais = match.
Makoto
icon := liked ? "♥" : "♡"TypeScript
const icon = liked ? "♥" : "♡";loop unificado (infinito / while / for-each / range)
Section titled “loop unificado (infinito / while / for-each / range)”Status: implementado
O cabeçalho decide: nada = infinito; expressão booleana = while; x in xs = for-each (coleção, gerador,
range); 0..8 exclusivo, 0..=8 inclusivo.
Makoto
loop { break }loop n > 0 { n -= 1 }loop x in xs { io.println(x) }loop i in 1..=3 { io.println(i) }TypeScript
for (const x of xs) console.log(x);Loop estilo C: loop var i := 0; ...
Section titled “Loop estilo C: loop var i := 0; ...”Status: implementado
Três cláusulas separadas por ;, parênteses opcionais. O contador precisa ser var.
Makoto
loop var i := 0; i < 3; i = i + 1 { io.println(i) }TypeScript
for (let i = 0; i < 3; i++) console.log(i);break/continue com label
Section titled “break/continue com label”Status: implementado
Label estilo Go antes do loop (outer:). break/continue nunca carregam valor.
Makoto
outer: loop i in 0..3 { loop j in 0..3 { if j == 1 { continue outer }; if i == 2 { break outer } }}TypeScript
outer: for (...) { for (...) { break outer; } }loop como valor
Section titled “loop como valor”Status: implementado
x := loop { ... return v }: o return dentro do loop-valor entrega ao binding (não sai da função). Todo
caminho de saída precisa entregar.
Makoto
x := loop { return 42 }TypeScript
let x: number; for (;;) { x = 42; break; }match (switch/match) com or-pattern e _
Section titled “match (switch/match) com or-pattern e _”Status: implementado
Sem fallthrough; | junta casos; _ é o default e é obrigatório em domínio aberto (int, string) e
proibido em domínio fechado (enum) — adicionar variante quebra o build até tratar. Braços separados por
quebra de linha (ou ; na mesma linha).
Makoto
match code { 200 | 201 | 204 => ok() 404 | 410 => gone() _ => other()}TypeScript
switch (code) { case 200: case 201: case 204: ok(); break; default: other(); }match como valor e return match
Section titled “match como valor e return match”Status: implementado
Braço de match não entrega sozinho. Em posição de valor, cada braço faz return v (entrega ao binding);
return match x { A => B } distribui o return pros braços.
Makoto
v := match n { 0 => return "zero"; _ => return "outro" }fn area(s: Shape) -> float { return match s { Circle(r) => 3.14 * r * r; Rect(w, h) => w * h }}TypeScript
const v = n === 0 ? "zero" : "outro";defer (limpeza LIFO, args congelados)
Section titled “defer (limpeza LIFO, args congelados)”Status: implementado
Roda na saída do escopo (inclusive return antecipado), vários em LIFO. Os argumentos são avaliados no
defer; só a chamada é adiada. Também roda quando o processo morre e quando um gerador é abandonado.
Makoto
@mm(none)fn work() { let buf: [*]u8 = mm.alloc(64) defer free(buf) // roda em qualquer caminho de saída var n := 0 defer io.println("n={{n}}") // imprime n=0 (arg congelado) n = 9}TypeScript
using f = open("cfg"); // TS 5.2 explicit resource managementruntime.panic (aborta o processo)
Section titled “runtime.panic (aborta o processo)”Status: implementado
panic não é global: vive em runtime (use runtime). Mata só o processo Makoto atual — os defers
rodam, a memória do processo é liberada e o erro chega ao supervisor.
Makoto
use runtimeif bad { runtime.panic("estado inválido") }TypeScript
throw new Error("estado inválido");Funções, lambdas, métodos e despacho
Section titled “Funções, lambdas, métodos e despacho”Método = função com receiver explícito; x.f() é UFCS (= f(x)). Lambda só existe como argumento (não
dá pra guardar em variável). Função aninhada é escopo, não closure. “Overload” é explícito (function set /
match T).
Declaração de função
Section titled “Declaração de função”Status: implementado
fn nome(p: T, ...) -> R { ... }. Sem retorno = sem ->. Modificadores ortogonais: pub, comptime,
unsafe, @generator.
Makoto
fn add(a: int, b: int) -> int { return a + b }pub fn greet(name: string) { io.println("oi {{name}}") }TypeScript
function add(a: number, b: number): number { return a + b; }Parâmetros com valor padrão
Section titled “Parâmetros com valor padrão”Status: implementado
fn f(x: int = 5): chamar f() usa o 5. É assim que “mesmo nome, aridade diferente” funciona.
Makoto
fn f(x: int = 5) -> int { return x * 2 }f() // 10f(1) // 2TypeScript
function f(x: number = 5): number { return x * 2; }Variádico ...T (e spread na chamada)
Section titled “Variádico ...T (e spread na chamada)”Status: parcial
Parâmetro xs: ...int junta N argumentos (vira slice). Heterogêneo e tipado: ...Display. O spread no
ponto de chamada f(...xs) está desenhado mas ainda não implementado (parser rejeita ... na chamada).
Makoto
fn sum(xs: ...int) -> int { var t := 0; loop x in xs { t += x }; return t }sum(1, 2, 3, 4)fn log(items: ...Display) { loop i in items { io.println(i.to_string()) } }TypeScript
function sum(...xs: number[]): number { return xs.reduce((a, b) => a + b, 0); }Funções não se guardam em variável
Section titled “Funções não se guardam em variável”Status: não existe (por design)
f := fn(x) => ... e g: fn(int) -> int = double são erro. Quem guarda função é um parâmetro de tipo
função (callback). Quer reusar? Dê nome (fn nome) e chame pelo nome.
Makoto
fn apply(f: fn(int) -> int, x: int) -> int { return f(x) }fn double(n: int) -> int { return n * 2 }apply(double, 5)TypeScript
const double = (n: number) => n * 2;Lambda (só como argumento)
Section titled “Lambda (só como argumento)”Status: implementado
Duas formas, ambas só em posição de argumento: fn(x) => expr (o => entrega) e fn(x) { ...; return v }.
Tipo de retorno inferido ou anotado (fn(x: int) -> int => x + 1). Captura por referência o escopo.
Makoto
use collectionsys := collections.map(xs, fn(n: int) => n * 10) // xs: List[int]zs := collections.filter(xs, fn(n: int) { return n % 2 == 0 })TypeScript
const ys = xs.map((n: number) => n * 10);Função aninhada (escopo, não captura)
Section titled “Função aninhada (escopo, não captura)”Status: implementado
fn a() { fn b() {...} } é válido: b só é visível dentro de a, pode recursar e ser chamada antes da
declaração — mas não enxerga os locais de a (tudo chega por parâmetro). Capturar escopo é exclusivo do lambda.
Makoto
fn outer(x: int) -> int { fn helper(v: int) -> int { return v + 1 } return helper(x)}TypeScript
function outer(x: number) { function helper(v: number) { return v + 1; } return helper(x); }Métodos: receiver explícito + UFCS
Section titled “Métodos: receiver explícito + UFCS”Status: implementado
fn (p: Point) norm() (por valor) ou fn (p: *Point) move() (por ponteiro — acesso a campo exige deref
explícito (*p).x, não há auto-deref). p.norm() é UFCS: igual a norm(p). Pipeline com . — não existe |>.
Makoto
decl Counter { n: int }fn (c: Counter) get() -> int { return c.n }fn (c: *Counter) inc() { (*c).n = (*c).n + 1 } // ponteiro NÃO auto-deref: `*` explícitoTypeScript
class Counter { n = 0; get() { return this.n; } inc() { this.n++; } }Função associada / construtor: fn (T) new()
Section titled “Função associada / construtor: fn (T) new()”Status: implementado
Receiver sem nome = associada ao tipo (sem instância). Chamada por ponto: Arena.new(). Não existe static.
Makoto
decl Stack { n: int }fn (Stack) new() -> Stack { return Stack{n: 0} }s := Stack.new()TypeScript
class Stack { static new(): Stack { return new Stack(); } }Function set: fn nome = { f1, f2 }
Section titled “Function set: fn nome = { f1, f2 }”Status: implementado
Junta funções existentes num roteador por tipo, resolvido em compile-time (análogo ao proc{} do Odin). As
assinaturas precisam ser distinguíveis; ambiguidade é erro na declaração do set.
Makoto
fn bool_s(b: bool) -> string { return b ? "true" : "false" }fn int_s(i: int) -> string { return "int" }fn to_s = { bool_s, int_s }to_s(true); to_s(3)TypeScript
function toS(x: boolean): string; function toS(x: number): string; function toS(x: any) {...}match T: despacho por tipo em genérico
Section titled “match T: despacho por tipo em genérico”Status: implementado
Genérico com whitelist [T: {bool, int}] + match T no corpo: o compilador escolhe o braço e monomorfiza.
Domínio fechado _ proibido.
Makoto
fn show[T: {bool, int}](x: T) -> string { match T { bool => return "B" int => return "I" }}TypeScript
function show(x: boolean | number) { return typeof x === "boolean" ? "B" : "I"; }Tipos: struct, enum, interface, union
Section titled “Tipos: struct, enum, interface, union”Uma palavra só, decl, declara os três contratos — o corpo diz qual: campos nome: T = struct (produto),
variantes Nome(...) = enum (soma), assinaturas fn ... = interface (comportamento). union fica de fora
(é outro sistema: reinterpretação de bytes, unsafe).
Struct: decl Nome { campo: T; ... }
Section titled “Struct: decl Nome { campo: T; ... }”Status: implementado
Produto. Campos separados por ; (ou quebra de linha). Literal nomeado P{x: 1; y: 2} ou posicional
P{1; 2} — atenção: ; entre campos (vírgula é pra grupo de valores, como []int{1, 2}).
O compilador reordena os campos pra reduzir padding (use @repr(c) pra layout fiel ao C).
Makoto
decl Vec2 { x: float; y: float }a := Vec2{1.0; 2.0}b := Vec2{x: 1.0; y: 2.0}TypeScript
interface Vec2 { x: number; y: number }const b: Vec2 = { x: 1.0, y: 2.0 };Visibilidade de campo: privado por padrão, pub expõe
Section titled “Visibilidade de campo: privado por padrão, pub expõe”Status: implementado
Código colabora (fn sem marca = visível no módulo); dados se protegem: campo sem marca é privado ao módulo,
pub campo expõe pra outros módulos. Campo nunca tem var (mutabilidade é do lugar).
Makoto
decl User { id: int // privado ao módulo pub email: string // visível fora do módulo}TypeScript
class User { private id: number; email: string; }Enum / tipo soma com payload
Section titled “Enum / tipo soma com payload”Status: implementado
Cada variante pode carregar dados (como uma struct atrás de um rótulo). Construção com o tipo: Shape.Circle(2.0);
no padrão o Shape. some. Exaustividade: match sobre enum cobre todas as variantes e proíbe _.
Makoto
decl Shape { Circle(float); Rect(float, float) }s: Shape = Shape.Circle(2.0)match s { Circle(r) => io.println(r) Rect(w, h) => io.println(w * h)}TypeScript
type Shape = { kind: "circle"; r: number } | { kind: "rect"; w: number; h: number };switch (s.kind) { case "circle": ...; case "rect": ...; }Diferença: União discriminada do TS é o equivalente;
neverno default dá exaustividade.
Conjunto de variantes: Shape{Circle}
Section titled “Conjunto de variantes: Shape{Circle}”Status: implementado
Em posição de tipo, Nome{Variantes} é um subconjunto do enum: Shape{Circle} = “um Shape, garantidamente
Circle”. Construir uma variante dá o tipo mais estreito, que alarga sozinho pro superconjunto. É a mesma
mecânica dos conjuntos de erro error{...}.
Makoto
fn only_circle(s: Shape{Circle}) -> float { return 1.0 }c := Shape.Circle(1.0) // tipo: Shape{Circle}only_circle(c) // ok; um Shape.Rect não compilariaTypeScript
function onlyCircle(s: Extract<Shape, { kind: "circle" }>) {...}Literal de enum com ponto: .Red
Section titled “Literal de enum com ponto: .Red”Status: implementado
Onde o tipo esperado é um enum conhecido, .Variante basta (inferido). Sem contexto, escreva completo (Color.Red).
Makoto
decl Color { Red; Green; Blue }fn paint(c: Color) -> int { return match c { Red => 1; Green => 2; Blue => 3 } }paint(.Green)let c: Color = .BlueTypeScript
enum Color { Red, Green, Blue }paint(Color.Green);Interface estrutural (satisfação implícita)
Section titled “Interface estrutural (satisfação implícita)”Status: implementado
decl Nome { fn metodo() -> T } — só assinaturas, sem métodos default. Um tipo satisfaz implementando os
métodos (estilo Go), sem implements. Comportamento derivado = função genérica livre sobre o bound.
Makoto
decl Shower { fn show() -> string }decl P { x: int }fn (p: P) show() -> string { return "P{{p.x}}" }fn greet[T + Shower](v: T) -> string { return "hi " + v.show() }TypeScript
interface Shower { show(): string }// estrutural também: P satisfaz sem declararDiferença: TS também é estrutural — é o modelo mais parecido.
Valor de interface (vtable) × bound genérico (monomorfização)
Section titled “Valor de interface (vtable) × bound genérico (monomorfização)”Status: implementado
d: Shower = p guarda um fat pointer {dados, vtable} (tipo concreto apagado; lista heterogênea possível).
[T + Shower] monomorfiza (chamada direta, sem vtable). Método genérico só via bound, nunca via valor de interface.
Makoto
d: Shower = p // dinâmico (vtable)xs := []Display{p, 3, "s"} // heterogêneo e tipadogreet(p) // estático (monomorfizado)TypeScript
const xs: Array<{ toString(): string }> = [p, 3, "s"];Sem Any: ‘qualquer tipo’ é um bound
Section titled “Sem Any: ‘qualquer tipo’ é um bound”Status: não existe (por design)
Não existe Any/Object/interface{}/void* seguro. Você diz “qualquer tipo que faça X”: ...Display,
[T + Serializable]. Interpolação {{x}} exige Display em compile-time.
Makoto
fn log(xs: ...Display) { loop x in xs { io.println(x.to_string()) } }log(1, "a", true)TypeScript
function log(...xs: unknown[]) {...} // ou anyInterface Display (to_string)
Section titled “Interface Display (to_string)”Status: implementado
decl Display { fn to_string() -> string }. Primitivos já implementam; implemente pro seu tipo e ganhe
interpolação {{p}} e qualquer genérico sobre Display.
Makoto
fn (p: P) to_string() -> string { return "P({{p.x}})" }io.println("{{p}}") // P(1)TypeScript
class P { toString(): string { return `P(${this.x})`; } }Composição: @embeds(Tipo, methods|fields|all)
Section titled “Composição: @embeds(Tipo, methods|fields|all)”Status: implementado
Sem herança: embutir um tipo e declarar o que é promovido. Promoção é transitiva, passa por campo ponteiro, e
colisão entre irmãos é erro (resolve pelo caminho nomeado). b as A extrai a parte embutida por valor
(tipagem nominal: B não vira A sozinho).
Makoto
decl A { v: int }@embeds(A, all)decl B { a: A; w: int }fn take(value: A) -> int { return value.v }b := B{a: A{v: 7}; w: 1}io.println(b.v) // campo promovidotake(b as A) // extração explícitaTypeScript
interface B extends A { w: number }Tipos recursivos exigem indireção
Section titled “Tipos recursivos exigem indireção”Status: implementado
Struct/enum não pode conter a si mesmo por valor (tamanho infinito). A auto-referência passa por *T, [*]T,
[]T ou Optional[*T]. O compilador reporta o ciclo, nunca faz boxing escondido.
Makoto
decl Node { val: int; next: Optional[*Node] }TypeScript
interface Node { val: number; next?: Node }Tuplas (a, b)
Section titled “Tuplas (a, b)”Status: implementado
Grupo posicional de valores: (10, "x"). Desestruturação x, y := t. Retorno sem erro com vários valores.
Makoto
t := (1, "x")x, y := tTypeScript
const t: [number, string] = [1, "x"];unsafe union (reinterpretação de bytes)
Section titled “unsafe union (reinterpretação de bytes)”Status: implementado
Campos sobrepostos, sem discriminante (quem sabe o campo ativo é você). Construir e ler são operações unsafe
com assume "motivo" (ou assert provável). Union com ponteiro não pode viver sob @mm(gc).
Makoto
unsafe union FloatBits { f: f32; bits: u32 }fb := unsafe FloatBits{f: 1.5} assume "construir a union pra ler os bits"y: u32 = unsafe fb.bits assume "reinterpretação intencional float->bits"TypeScript
new Uint32Array(new Float32Array([1.5]).buffer)[0]Layout: @repr(c) e @repr(packed)
Section titled “Layout: @repr(c) e @repr(packed)”Status: implementado
@repr(c) desliga a reordenação e dá layout fiel ao C (obrigatório pra struct que cruza a fronteira FFI).
@repr(packed) = bit-packed estilo Zig (cada campo com sua largura exata; u3+u5 num byte). Layout
observável em compile-time: mem.size_of[T](), mem.align_of[T](), mem.offset_of[T]("campo").
Makoto
@repr(c)decl CPoint { x: i32; y: i32 }@repr(packed)decl Flags { a: u3; b: u5 }TypeScript
// sem layout: DataView sobre ArrayBufferEnum de flags: @flags
Section titled “Enum de flags: @flags”Status: planejado
Desenhado (§17): @flags decl Perms { Read; Write; Exec } tornaria o enum int-backed e bitmaskável
(Read | Write). Ainda não implementado (o checker rejeita o decorator). Hoje: constantes inteiras ou enum
vindo do C (que já é bitmaskável).
Makoto
@flags decl Perms { Read; Write; Exec } // planejadop := Perms.Read | Perms.WriteTypeScript
enum Perms { Read = 1, Write = 2, Exec = 4 }const p = Perms.Read | Perms.Write;Coleções e Optional
Section titled “Coleções e Optional”Coleções são valores: bind copia (o compilador elide a cópia quando ninguém escreve). Compartilhar é pedido
explicitamente: view xs[a..b] ou ponteiro. []T é slice (ponteiro + tamanho), [N]T array fixo, [*]T
buffer cru sem tamanho. List[T] é builtin; Map/Set e as HOFs vêm de collections.
Slice e array: []T, [N]T, [_]T
Section titled “Slice e array: []T, [N]T, [_]T”Status: implementado
Literal com vírgula: []int{1, 2, 3}, [3]int{...}, [_]int{...} (tamanho inferido). Indexação checada
(panic fora dos limites). Tamanho é o campo .len.
Makoto
xs := []int{1, 2, 3}arr := [3]int{1, 2, 3}inf := [_]int{4, 5}io.println(xs.len)TypeScript
const xs: number[] = [1, 2, 3];Semântica de valor (bind copia)
Section titled “Semântica de valor (bind copia)”Status: implementado
var ys := xs é cópia independente; escrever em ys não muda xs. Construir struct com slice copia pro campo.
Makoto
xs := []int{1, 2, 3}var ys := xsys[0] = 9 // xs continua [1, 2, 3]TypeScript
const ys = [...xs];View xs[a..b] × cópia xs[a:b]
Section titled “View xs[a..b] × cópia xs[a:b]”Status: implementado
.. = view O(1) (compartilha o backing; escrever pela view escreve no original). : = cópia O(n), independente.
Mesma regra pra array, slice e string (em string com unidade: .bytes/.chars/.codepoints).
Makoto
var xs := []int{1, 2, 3}var v := xs[..] // view do slice inteirov[0] = 9 // xs agora é [9, 2, 3]c := xs[0:2] // cópia de 2 elementosTypeScript
const c = xs.slice(0, 2);List[T] (vetor dinâmico)
Section titled “List[T] (vetor dinâmico)”Status: implementado
Builtin: List.new(), push, len(), at(i) (garante; panic fora) e get(i) ( Optional[T]). Nomes
honestos: get = “talvez”, at = “eu garanto”.
Makoto
var xs: List[int] = List.new()xs.push(1)xs.push(2)io.println(xs.at(0))io.println(xs.get(9).or(-1))TypeScript
const xs: number[] = []; xs.push(1);Map e Set (collections)
Section titled “Map e Set (collections)”Status: parcial
Implementado como tipos + funções livres em collections: map_new/map_put/map_get/map_has/map_size,
set_new/set_add/set_has/set_size. Qualquer tipo com == serve de chave (struct incluída, sem declarar nada).
Implementação atual: lista associativa (busca linear) — o hash-map do design, com hash derivado por reflexão,
e a API em métodos (Map[K, V](), m.set(k, v), get_or_insert) ainda não existem.
Makoto
use collectionsvar m := collections.map_new[string, int]()collections.map_put(mut m, "a", 1)v := collections.map_get(m, "a").or(0)var s := collections.set_new[int]()collections.set_add(mut s, 3)TypeScript
const m = new Map<string, number>(); const s = new Set<number>();Funções de alta ordem (collections.map/filter/reduce...)
Section titled “Funções de alta ordem (collections.map/filter/reduce...)”Status: implementado
Sobre List[T]: map filter reduce any all count reverse find find_index index_of contains take drop concat flat_map. Use a forma qualificada collections.filter(xs, f) (o UFCS xs.filter(f) ainda falha no lower).
Makoto
use collectionsys := collections.map(xs, fn(n: int) => n * 10)evens := collections.filter(xs, fn(n: int) => n % 2 == 0)total := collections.reduce(xs, 0, fn(a: int, n: int) => a + n)TypeScript
const ys = xs.map((n: number) => n * 10);Optional[T]: some/none, .or, .or_panic
Section titled “Optional[T]: some/none, .or, .or_panic”Status: implementado
Ausência tipada. Abrir com match (some(v) => / none =>), com default .or(d), ou abortar honestamente
.or_panic() (o nome diz a consequência — não existe unwrap). .is_present() testa.
Makoto
match find(xs, 9) { some(i) => io.println(i) none => io.println("ausente")}v := find(xs, 5).or(-1)w := find(xs, 5).or_panic()TypeScript
const v: number = find(xs, 5) ?? -1;Strings e texto
Section titled “Strings e texto”string é sempre UTF-8 válido (tipo builtin composto, não é []byte). Não existe s[i]: a unidade é
sempre explícita (.bytes, .codepoints, .chars = grafemas). Comparação por operador é byte-a-byte;
semântica/locale é por método.
string = UTF-8 válido
Section titled “string = UTF-8 válido”Status: implementado
Bytes possivelmente inválidos são []byte. Validação na fronteira (string.from[u8](bytes) Result).
Makoto
s := "héllo"io.println(s.bytes.len) // 6 bytesio.println(s.codepoints.len) // 5 codepointsTypeScript
const s: string = "héllo";Acesso por unidade: .bytes[i], .chars[i], .codepoints[i]
Section titled “Acesso por unidade: .bytes[i], .chars[i], .codepoints[i]”Status: implementado
s[2] e s[2..6] puros são erro de compilação (“sobre O QUÊ?”). Cada acesso tem forma curta e método:
s.bytes[2]/s.byte_at(2), s.chars[2..6]/s.chars_view(2, 6), s.codepoints[2:6]/s.codepoints_copy(2, 6).
Makoto
s := "héllo"io.println(s.chars[1]) // é (grafema)b := s.bytes[0] // bytev := s.bytes[0..2] // view por bytesTypeScript
[...s][1]Interpolação {{ expr }}
Section titled “Interpolação {{ expr }}”Status: implementado
Qualquer expressão dentro de {{ }}; formata via Display (checado em compile-time). \{{ é chave literal.
Formatação por método ({{x.pad(8)}}, {{pi.fixed(2)}}, {{n.hex()}}) está desenhada, ainda não implementada.
Makoto
name := "Ana"io.println("Olá {{name}}, {{ 2 + 3 }} itens")TypeScript
console.log(`Olá ${name}, ${2 + 3} itens`);Comparação: operadores = bytes; .equals(_, .semantic) = Unicode
Section titled “Comparação: operadores = bytes; .equals(_, .semantic) = Unicode”Status: implementado
==/< são igualdade/ordem lexicográfica de bytes. Igualdade com normalização Unicode: a.equals(b, .semantic).
Ordem alfabética por idioma: locale.Collator.new(.pt).order(a, b) Ordering. Não existe ===.
Makoto
a == b // bytes"é".equals("é", .semantic) // NFCuse localec := locale.Collator.new(.en)o := c.order("a", "b") // Ordering.LessTypeScript
new Intl.Collator("pt").compare(a, b);Mutação e crescimento (var + append)
Section titled “Mutação e crescimento (var + append)”Status: implementado
Não há StringBuilder: var s + s.append(...) cresce dobrando o buffer. Concatenação com +.
Makoto
var s := "ab"s.append("c")t := s + "!"TypeScript
let s = "ab"; s += "c";Conversões de encoding: to[u16], string.from[u16]
Section titled “Conversões de encoding: to[u16], string.from[u16]”Status: implementado
UTF-8 sai direto de .bytes. Outros encodings são explícitos e alocam: s.to[u16]() []byte (LE, sem BOM);
string.from[u8|u16|u32](bytes) Result. bytes.as_string_unchecked() pula a validação (unsafe).
BOM/endianness: pacote unicode (swap16, with_bom16, strip_bom16…).
Makoto
e := "hé".to[u16]()back := string.from[u16](e) catch |err| { return }TypeScript
new TextDecoder("utf-16le").decode(bytes);Ponte com C: .to_cstring() / from_cstring
Section titled “Ponte com C: .to_cstring() / from_cstring”Status: parcial
s.to_cstring() dá bytes NUL-terminados (implementado). A volta, from_cstring(p) (char* Result[string]
validando UTF-8), está desenhada mas não implementada. Hoje um char* vindo do C chega cru (*c.<mod>.char).
Makoto
v := unsafe c.stdlib.getenv("HOME".to_cstring()) assume "getenv com string válida"match v { some(p) => io.println("definida"); none => io.println("ausente") }TypeScript
process.env.HOME ?? ""Utilitários: pacote strings
Section titled “Utilitários: pacote strings”Status: implementado
is_empty byte_len starts_with ends_with index_of contains repeat split join trim_start trim_end trim replace to_upper to_lower (escritos em Makoto, na stdlib).
Makoto
use stringsparts := strings.split("a,b,c", ",")s := strings.join(parts, "-")u := strings.to_upper("abc")TypeScript
"a,b,c".split(",").join("-");Erros: Result, conjuntos de erro, catch
Section titled “Erros: Result, conjuntos de erro, catch”Dois eixos separados: Result[T, E] (deu certo ou não) e error{...} (quais falhas existem — conjunto fechado
e estrutural). Trailers: catch |e| trata como unidade; match |e| ramifica por variante; match sobre o
Result inteiro é o fork que recupera com valor.
Result[T, E] com Ok/Err
Section titled “Result[T, E] com Ok/Err”Status: implementado
Enum builtin { Ok(T), Err(E) }. Sucesso é sempre explícito (return Ok(x)); erro propagado ganha açúcar
(return e embrulha em Err).
Makoto
fn risky(n: int) -> Result[int, error{Neg, Zero}] { if n < 0 { return Err(error.Neg) } if n == 0 { return Err(error.Zero) } return Ok(n)}TypeScript
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };Fork: match sobre o Result (recuperar com valor)
Section titled “Fork: match sobre o Result (recuperar com valor)”Status: implementado
Cobre Ok e cada variante de erro, exaustivo (sem _ em conjunto fechado). É o lugar de recuperar com
um fallback.
Makoto
match load(p) { Ok(v) => io.println(v) Err(error.NotFound) => io.println("não achou") Err(error.Denied) => io.println("negado")}TypeScript
const r = load(p); if (r.ok) ... else switch (r.error) {...}Trailer catch |e| (propagar / abortar)
Section titled “Trailer catch |e| (propagar / abortar)”Status: implementado
O Ok vai pro binding; o bloco trata o erro e precisa escapar (return, break, panic) quando há
binding. return e propaga (vira Err(e)). Não existe ?.
Makoto
v := risky(x) catch |e| { return e } // propagaw := risky(x) catch |e| { runtime.panic("falhou") } // abortaTypeScript
const r = risky(x); if (!r.ok) return r;Trailer match |e| (ramificar por variante)
Section titled “Trailer match |e| (ramificar por variante)”Status: implementado
Açúcar de catch |e| { match e {...} }. Só vale com união (≥2 variantes); cada braço escapa quando há binding.
Makoto
v := risky(n) match |e| { Neg => { io.println("negativo"); return e } Zero => return e}TypeScript
if (!r.ok) switch (r.error) { case "Neg": ...; case "Zero": ...; }Conjuntos de erro error{...} e união com spread
Section titled “Conjuntos de erro error{...} e união com spread”Status: implementado
Estruturais e fechados; o estreito cabe no largo sem conversão. Compor = unir: error{open..., parse...} junta
as variantes dos retornos de open e parse.
Makoto
fn load(p: string) -> Result[int, error{open..., parse...}] { f := open(p) catch |e| { return e } d := parse(f) catch |e| { return e } return Ok(d)}TypeScript
type LoadErr = OpenErr | ParseErr;Sucesso sem valor: retornar só o conjunto de erro
Section titled “Sucesso sem valor: retornar só o conjunto de erro”Status: implementado
Não há unit/(): fn send() -> error{Full} — sucesso é a ausência de erro (return puro). Descartar
sem tratar é erro; trate com catch (o sucesso segue pra próxima linha).
Makoto
fn send_it(ok: bool) -> error{Full} { if !ok { return error.Full } return}send_it(false) catch |e| { io.println("cheio") }TypeScript
function sendIt(ok: boolean): void {...}Erro aberto error (FFI / terceiros)
Section titled “Erro aberto error (FFI / terceiros)”Status: implementado
Sem {...}, error é o domínio aberto (conjunto desconhecido): não-exaustivo, exige _/Err(e) genérico.
Makoto
fn ext() -> Result[int, error] { return Err(error.Whatever) }match ext() { Ok(v) => io.println(v); Err(e) => io.println("erro externo") }TypeScript
catch (e: unknown) {...}error{}: nunca falha
Section titled “error{}: nunca falha”Status: implementado
Conjunto vazio (inabitado): Result[T, error{}] diz na assinatura que o Err é impossível — útil pra
implementar interface falível com um tipo que nunca erra.
Makoto
fn never_fails() -> Result[int, error{}] { return Ok(1) }TypeScript
type R = { ok: true; value: number };Variante de erro com payload inline
Section titled “Variante de erro com payload inline”Status: planejado
Desenhado (§7): error{HTTP500, PortError(int)} inline, aberto com match como qualquer variante. Ainda não
parseia. Hoje: declare um decl enum com payload e use-o como E do Result.
Makoto
fn port(n: int) -> Result[int, error{HTTP500, PortError(int)}] // planejadoTypeScript
type E = { kind: "HTTP500" } | { kind: "PortError"; port: number };Memória: gerenciamento sem cor, ponteiros, ownership
Section titled “Memória: gerenciamento sem cor, ponteiros, ownership”Cada alocação carrega o seu MemoryManager (a estratégia viaja com o objeto), então gc, arena, none e
c se misturam sem colorir assinaturas. @mm(...) define a estratégia padrão para novas alocações num
contexto (processo, função, bloco, objeto). Sem borrow checker: um gate local decidível pega leak/double-free/
use-after-free no caminho manual, e @consume/@transfer movem ownership.
Padrão: @mm(gc) (coletor)
Section titled “Padrão: @mm(gc) (coletor)”Status: implementado
Sem anotação, tudo é gerenciado pelo coletor (mark-sweep, raízes precisas via shadow stack). free(p) sob gc
é no-op silencioso — o mesmo código roda em qualquer estratégia.
Makoto
xs := []int{1, 2, 3} // coletado automaticamenteTypeScript
const xs = [1, 2, 3];Manual: @mm(none) + mm.alloc + free
Section titled “Manual: @mm(none) + mm.alloc + free”Status: implementado
Estilo C, opt-in. mm.alloc(n) (ou mm.alloc(n, align)) dá um [*]T; free(p) devolve pelo manager do
próprio objeto (o compilador preenche o tamanho).
Makoto
use mm@mm(none)fn main() { let a: [*]int = mm.alloc(4) a[0] = 7 free(a)}TypeScript
// sem gerenciamento manualGate local de ownership (leak/double-free/UAF = erro de compilação)
Section titled “Gate local de ownership (leak/double-free/UAF = erro de compilação)”Status: implementado
Onde alocação e free estão na mesma função e o ponteiro não escapa, o tempo de vida é decidível: esquecer o
free num caminho, liberar duas vezes ou usar depois de liberar não compila. Sem anotação nenhuma.
Makoto
@mm(none)fn main() { let a: [*]int = mm.alloc(4) io.println("sem free")}// erro: memory leak: "a" was allocated with @mm(none) and is never freed on some pathTypeScript
Arena: @mm(arena) { ... }
Section titled “Arena: @mm(arena) { ... }”Status: implementado
Bloco com uma arena anônima nova: nasce no {, morre no } (liberação em massa). Sub-arena = bloco aninhado.
Operações de região ancoradas num ponteiro: mm.used(p), mm.reset(p), mm.release(p), mm.resize(p, n).
Makoto
use mmfn handle() { @mm(arena) { let a: [*]int = mm.alloc(8) io.println(mm.used(a)) } // tudo liberado aqui}TypeScript
@mm por processo / função / objeto
Section titled “@mm por processo / função / objeto”Status: implementado
Mesma diretiva em escopos diferentes; objeto recebido mantém o manager de origem (“colorless”).
Makoto
@mm(arena)spawn worker(data) // tudo que worker aloca vai pra uma arena@mm(none)fn low_level() { ... }TypeScript
@consume: parâmetro linear (ownership atravessa a função)
Section titled “@consume: parâmetro linear (ownership atravessa a função)”Status: implementado
Opt-in: o chamado passa a ser dono e precisa liberar/retornar/transferir exatamente uma vez; o chamador não pode usar depois. Guardar num campo de struct que escapa também quita a obrigação.
Makoto
fn free_it(p: [*]int @consume) { free(p) }TypeScript
@transfer: mover entre processos (invalida a origem)
Section titled “@transfer: mover entre processos (invalida a origem)”Status: implementado
Valor enviado por canal/spawn é copiado profundamente por padrão (heaps isolados). @transfer move em vez
de copiar: o objeto viaja com o seu manager e a variável de origem fica inválida (usar depois = erro).
Ponteiro cru *T nunca cruza processo.
Makoto
r := Rec{amount: 84; tags: []int{1, 2}}spawn worker(out, r @transfer)ch <- b @transferTypeScript
worker.postMessage(buf, [buf]);@promote(mm, shallow|deep): copiar pra outro manager
Section titled “@promote(mm, shallow|deep): copiar pra outro manager”Status: implementado
Otimização (não correção): copia o objeto para outro MemoryManager — ex.: tirar da arena antes de enviá-la, ou
trazer memória do C pro GC (p @promote(gc)). deep copia o subgrafo; a origem não é liberada sozinha.
Makoto
mine := p @promote(gc) // repatria memória vinda do Cchan <- result @promote(gc) @transferTypeScript
structuredClone(obj)@limit(16mb): teto de memória por processo
Section titled “@limit(16mb): teto de memória por processo”Status: implementado
Estourar o teto mata o processo; o motivo (OOM) chega ao supervisor no catch |e|. OOM é evento de processo,
não erro em cada push (isso colore tudo). Alocação com orçamento recuperável = descer pro mm.alloc.
Makoto
@limit(1kb)spawn worker() catch |e| { io.println("morreu por limite") }TypeScript
new Worker(f, { resourceLimits: { maxOldGenerationSizeMb: 16 } })Ponteiros *T: &lugar, *p, sem &local
Section titled “Ponteiros *T: &lugar, *p, sem &local”Status: implementado
Um só tipo de ponteiro (sem *const/*mut). Nasce de & sobre um lugar mutável com identidade no heap — um
var de módulo, ou o elemento/apontado de outro ponteiro (esse não exige var no handle: o binding do ponteiro e a
mutabilidade do apontado são independentes) — nunca de um local na pilha (pra mutar local use mut). Deref
explícito em toda leitura e escrita (sem auto-deref). Ponteiro nunca é nulo: Optional[*T]. Sem aritmética com +.
Makoto
let a: [*]int = mm.alloc(4)p := &a[1]*p = 5decl Node { val: int; next: Optional[*Node] }TypeScript
// sem ponteirosPointer-to-many [*]T (buffer cru)
Section titled “Pointer-to-many [*]T (buffer cru)”Status: implementado
N elementos contíguos que você possui, sem tamanho. Indexar é unchecked (unsafe); items[a..b] vira
slice (onde o tamanho nasce); avançar a base é p.offset(k) — nunca p + k.
Makoto
b := a.offset(1)x := unsafe b[0] assume "b aponta dentro do bloco de 4"TypeScript
new Int32Array(buf, 4)mut: passagem com write-back (inout)
Section titled “mut: passagem com write-back (inout)”Status: implementado
Argumento vai por valor por padrão. mut no parâmetro e na chamada = a função altera a variável do
chamador (que precisa ser var). Não é tipo, não escapa, é exclusivo (f(mut x, mut x) = erro); implementado
por referência (zero cópia).
Makoto
fn bump(v: mut int) { v = v + 1 }var x := 5bump(mut x) // x == 6TypeScript
// idemAutofree: free inserido após o último uso
Section titled “Autofree: free inserido após o último uso”Status: implementado
Onde a análise prova tempo de vida linear, o compilador insere o free logo após o último uso (tira o
objeto do mundo do GC). Nunca muda o que o programa computa. Ligado por padrão; --no-autofree desliga;
--autofree=default|conservative|optimized.
Makoto
// nada a escrever: é otimização do compilador// mk build --autofree=optimized app.mkoTypeScript
Analisador de memória consultivo (mk analyze, -Wmm, --strict-mm)
Section titled “Analisador de memória consultivo (mk analyze, -Wmm, --strict-mm)”Status: implementado
Um borrow-checker que você consulta: provado-fatal = erro; provável = aviso; poderia-ser-melhor = dica.
--strict-mm promove avisos a erro; falso positivo é silenciado no local com assume "motivo".
Makoto
// mk analyze app.mko -> relatório (erros, avisos, dicas)// mk build -Wmm app.mko -> liga os avisos// strict-mm true (no mk.project)TypeScript
Opções do coletor (--gc-roots, --gc-collector, --gc=wasmgc)
Section titled “Opções do coletor (--gc-roots, --gc-collector, --gc=wasmgc)”Status: parcial
--gc-roots=shadow (padrão, portável) ou stackmaps (LLVM statepoints, só host); --gc-collector=moving
(compactador; objetos fixos com @pin); --gc=wasmgc usa o GC do host WebAssembly (trilha em andamento).
Makoto
// mk build --gc-roots=stackmaps --gc-collector=moving app.mkoTypeScript
// GC do engineConcorrência: processos e canais
Section titled “Concorrência: processos e canais”A unidade de concorrência é o processo (estilo Erlang, compilado nativamente): heap privado, ciclo de vida
gerenciado, falha contida. Comunicação só por canal tipado (não há memória compartilhada nem @shared).
IO sem cor: o scheduler suspende só o processo que bloqueou.
spawn f(x) (fire-and-forget)
Section titled “spawn f(x) (fire-and-forget)”Status: implementado
Cria um processo leve. Não devolve handle (referência é por registro, @register). Argumentos cruzam por cópia
profunda (ou @transfer).
Makoto
spawn worker(data)TypeScript
new Worker(new URL("./worker.ts", import.meta.url)).postMessage(data);Canal: Channel.new() / Channel.new(64)
Section titled “Canal: Channel.new() / Channel.new(64)”Status: implementado
Primitiva do core (sem literal, sem zero-value). Sem argumento = rendezvous (send bloqueia até o receive); com capacidade = buffer (send em buffer cheio bloqueia = backpressure). Buffer pertence ao runtime.
Makoto
c: Channel[int] = Channel.new()b: Channel[int] = Channel.new(64)TypeScript
const { port1, port2 } = new MessageChannel();Direção no tipo: Channel[<-A, ->B]
Section titled “Direção no tipo: Channel[<-A, ->B]”Status: implementado
Perspectiva de quem segura: <-T = “eu envio T”, ->T = “eu recebo T”. Channel[T] = simétrico. O compilador
checa que os dois lados são complementares no spawn.
Makoto
fn worker(ch: Channel[<-string, ->int]) { // envia string, recebe int v := -> ch catch |e| { return } ch <- "got {{v}}"}TypeScript
// sem canais tipados na plataformaEnviar ch <- v / receber -> ch
Section titled “Enviar ch <- v / receber -> ch”Status: implementado
Receber devolve Result[T, error{ProcessDown}] (os remetentes morreram). Não existe close: o fim do
stream é a morte do processo que alimenta. Entrega FIFO por canal.
Makoto
ch <- 42v := -> ch catch |e| { return }loop { x := -> ch catch |e| { break } // produtor morreu: fim process(x)}TypeScript
port.postMessage(42);Request-reply síncrono <-> com timeout obrigatório
Section titled “Request-reply síncrono <-> com timeout obrigatório”Status: implementado
ch <-> req timeout(5s) envia e espera a resposta. Sem timeout(...) = erro de compilação (dois <-> cruzados
seriam deadlock garantido). Falha: Timeout | ProcessDown.
Makoto
reply := c <-> 21 timeout(1s) catch |e| { return }TypeScript
await Promise.race([request(port, 21), timeout(1000)]);Select = match { ... } timeout(d) { ... }
Section titled “Select = match { ... } timeout(d) { ... }”Status: implementado
match sem sujeito espera vários canais. Trailer timeout(d) roda se nada chega; timeout(0) = poll não
bloqueante. Com vários prontos, os braços se revezam (sem inanição e reprodutível — nem sempre o 1º, nem aleatório).
Makoto
match { v := -> a => io.println(v) v := -> b => io.println(v)} timeout(100ms) { io.println("nada chegou")}TypeScript
await Promise.race([next(a), next(b), sleep(100)]);Referência por nome: @register("nome") e grupos
Section titled “Referência por nome: @register("nome") e grupos”Status: implementado
spawn não devolve pid. Para inspecionar depois, registre: nome único que sobrevive a restart; ou
@register("handlers", group) pra endereçar por índice. Lookup: runtime.process("nome") ( Optional),
runtime.group("nome"), runtime.self().
Makoto
@register("db_writer")spawn db_writer(conn)match runtime.process("db_writer") { some(h) => io.println("vivo"); none => io.println("sumiu") }TypeScript
const workers = new Map<string, Worker>();Scheduler: --scheduler=throughput|deterministic
Section titled “Scheduler: --scheduler=throughput|deterministic”Status: implementado
Mesma API nos dois. throughput (padrão): work-stealing multi-core. deterministic: time-slicing rígido
estilo BEAM (tempo real). Ambos preemptam (reduções em back-edges de loop + monitor de tempo). FFI bloqueante é
absorvido no work-stealing; no determinístico, isole num processo.
Makoto
// mk build --scheduler=deterministic app.mkoTypeScript
// event loop single-threadSem memória compartilhada entre processos
Section titled “Sem memória compartilhada entre processos”Status: não existe (por design)
Não há @shared, mutex nem atomics de usuário. Dado compartilhado = um processo dono que serve pedidos via canal
(padrão “actor”). Dentro de um processo não há aliasing concorrente, por isso não há borrow checker.
Makoto
fn counter(ch: Channel[->int, <-int]) { var n := 0 loop { d := -> ch catch |e| { return }; n += d; ch <- n }}TypeScript
Atomics.add(sharedArray, 0, d);Supervisão e resiliência (let it crash)
Section titled “Supervisão e resiliência (let it crash)”Modelo BEAM sem expor link/monitor crus: a árvore de supervisão é a estrutura do código. O catch |e|
de um spawn é o monitor; morte de processo é um valor de erro. Ordem da morte: defers libera o @mm
entrega o erro.
Monitor = spawn f() catch |e| { ... }
Section titled “Monitor = spawn f() catch |e| { ... }”Status: implementado
O filho morre, quem fez spawn não morre e recebe o motivo. Sem anotação = one_for_one sem limite de restart.
Makoto
spawn worker(data) catch |e| { io.println("worker caiu") }TypeScript
worker.on("exit", (code: number) => {...});Motivo da morte: match |e| no spawn
Section titled “Motivo da morte: match |e| no spawn”Status: implementado
O erro é estruturado: Crashed(msg), Normal, Timeout, Overrun, OutOfMemory…
Makoto
spawn crasher() match |e| { Crashed(msg) => io.println("crashed: {{msg}}") error.Normal | error.Timeout | error.Overrun | error.OutOfMemory => io.println("outro")}TypeScript
worker.on("exit", (code) => {...});@supervisor(max_restarts: N, window: T)
Section titled “@supervisor(max_restarts: N, window: T)”Status: implementado
Sem catch: o runtime reinicia sozinho, limitado pela janela. Com catch: você decide
(runtime.supervisor.restart() / escalate(); não fazer nada = fica morto). Restart re-invoca com os mesmos
argumentos — canal passado como argumento reconecta; nome registrado sobrevive.
Makoto
@supervisor(max_restarts: 3, window: 10s)spawn worker(data) catch |e| { runtime.supervisor.restart()}TypeScript
// idemEstratégias: one_for_one / one_for_all / rest_for_one
Section titled “Estratégias: one_for_one / one_for_all / rest_for_one”Status: implementado
spawn individual = one_for_one; bloco spawn { a(); b() } catch |e| {...} = one_for_all (vivem e morrem
juntos; o catch externo dispara quando o grupo esgota); @supervisor(strategy: rest_for_one) = reinicia quem
veio depois (pipeline).
Makoto
spawn { stage_a() stage_b()} catch |e| { io.println("grupo esgotado") }
@supervisor(strategy: rest_for_one, max_restarts: 5, window: 30s)spawn { db_connection(); db_writer(); db_reader() } catch |e| { ... }TypeScript
// manualÁrvore por aninhamento
Section titled “Árvore por aninhamento”Status: implementado
Qualquer processo que faz spawn é supervisor implícito dos filhos. escalate() mata o atual e entrega o erro a
quem o supervisiona; em main sem supervisor, o programa sai com o erro.
Makoto
spawn http_server(cfg) catch |e| { runtime.supervisor.restart() }TypeScript
// manualCanais pela rede (net)
Section titled “Canais pela rede (net)”Não há “tipo canal de rede”: net.connect[T]/net.accept[T] devolvem um Channel[T] comum. A física aparece
nos tipos que já existem: o payload precisa ser Serializable (derivado automaticamente por reflexão) e
estabelecer a conexão devolve Result.
Servidor: net.listen + net.accept[T]
Section titled “Servidor: net.listen + net.accept[T]”Status: implementado
Cada conexão aceita vira um Channel[T]; trate cada cliente como um processo.
Makoto
use netlistener := net.listen("0.0.0.0:8080") catch |e| { return }loop { conn := net.accept[Msg](listener) catch |e| { continue } spawn handle(conn)}TypeScript
net.createServer((sock: net.Socket) => {...}).listen(8080);Cliente: net.connect[T]
Section titled “Cliente: net.connect[T]”Status: implementado
Mesma API de canal: <-, ->, select, timeout.
Makoto
chan := net.connect[Msg]("127.0.0.1:8080") catch |e| { return }chan <- requestreply := -> chan catch |e| { return }TypeScript
const sock = net.connect(8080, "127.0.0.1");Generics, comptime e reflexão
Section titled “Generics, comptime e reflexão”Generics e metaprogramação são a mesma coisa por baixo: comptime. [...] = parâmetros de compile-time
(tipos e valores), (...) = de runtime. type é um valor de compile-time; decl Box[T] é açúcar pra uma
comptime fn que devolve type. Restrições declaradas: + Interface (capacidade) e : {tipos} (whitelist).
Função genérica fn f[T](x: T)
Section titled “Função genérica fn f[T](x: T)”Status: implementado
Monomorfizada por tipo concreto. Argumentos de tipo inferidos onde os valores fixam; senão explícitos. Tudo ou
nada: não existe aplicação parcial (f[i32](p) pra f[T, V] é erro).
Makoto
fn id[T](x: T) -> T { return x }id(5) // T = int (inferido)id[int](5) // explícitoTypeScript
function id<T>(x: T): T { return x; }Tipo genérico decl Box[T] { v: T }
Section titled “Tipo genérico decl Box[T] { v: T }”Status: implementado
Box{5} infere T pelo campo; Box[int]{5} explícito. Enum genérico: Tree[int].Leaf(5) ou Tree.Leaf(5).
Makoto
decl Box[T] { v: T }b := Box{5}c := Box[int]{6}TypeScript
interface Box<T> { v: T }const b: Box<number> = { v: 5 };Restrição por capacidade: [T + Display]
Section titled “Restrição por capacidade: [T + Display]”Status: implementado
“T precisa saber fazer X”. Checado na fronteira da instanciação com erro limpo (não 3 camadas dentro).
Makoto
fn show[T + Display](x: T) -> string { return x.to_string() }TypeScript
function show<T extends { toString(): string }>(x: T) { return x.toString(); }Whitelist de tipos: [T: {i8, u16}]
Section titled “Whitelist de tipos: [T: {i8, u16}]”Status: implementado
“T só pode ser um destes” — de primeira classe (Rust precisaria de sealed trait). Combina com interface:
[T: {i8, u16} + Display]. Forma plana: nada de |, & ou parênteses.
Makoto
fn small[T: {i8, u16}](x: T) -> T { return x }fn both[T: {i8, u16} + Display](x: T) -> string { return x.to_string() }TypeScript
function small<T extends number>(x: T) { return x; } // união de literais de tipo não existe p/ primitivosParâmetros de valor: [N: int]
Section titled “Parâmetros de valor: [N: int]”Status: implementado
[...] aceita valores de compile-time, não só tipos: cada valor distinto gera uma especialização
(monomorfização por valor). Base da camada prática de tipos dependentes (Vec[T, n]).
Makoto
fn zeros[N: int]() -> int { return N * 2 }zeros[4]() // especializado com N = 4TypeScript
// n/a (só tipos literais)Famílias de tipo / HKT raso: C[_]
Section titled “Famílias de tipo / HKT raso: C[_]”Status: implementado
Abstrair sobre o construtor não aplicado (List, Box) pra escrever map/collect uma vez. Cortes: só aridade
1 e 2, unificação de 1ª ordem, sem busca implícita de instância (resolvido por interface + monomorfização).
Makoto
decl Mappable[C[_]] { fn fmap[A, B](xs: C[A], f: fn(A) -> B) -> C[B] }decl Box[T] { v: T }fn (b: Box[T]) fmap[U](f: fn(T) -> U) -> Box[U] { return Box[U]{f(b.v)} }fn pmap[C[_] + Mappable, A, B](c: C[A], f: fn(A) -> B) -> C[B] { return c.fmap(f) }TypeScript
// sem HKT nativo (truque com interfaces URI -> tipo, estilo fp-ts)comptime fn, const e comptime var
Section titled “comptime fn, const e comptime var”Status: implementado
comptime fn roda inteira no compilador e some do binário (só em contexto de compile-time: const, [...],
outra comptime fn). comptime var = rascunho mutável da compilação. Comptime é puro: sem IO, spawn, canais.
Makoto
comptime fn repeat(s: string, n: int) -> string { comptime var acc := "" comptime var i := 0 loop { if i == n { break }; acc = acc + s; i = i + 1 } return acc}const R := repeat("ab", 3) // "ababab" embutido no binárioTypeScript
// n/a (só tipos condicionais)comptime if / comptime match / comptime loop / comptime {}
Section titled “comptime if / comptime match / comptime loop / comptime {}”Status: implementado
Resolvidos pelo compilador: só o ramo escolhido é emitido (compilação condicional). comptime loop desenrola
(necessário quando o tipo varia por iteração, ex.: campos de struct).
Makoto
use archcomptime if arch.current == .x86_64 { io.println("x86") }TypeScript
Reflexão de compile-time: reflect(T)
Section titled “Reflexão de compile-time: reflect(T)”Status: implementado
use compiler.{reflect, fail}. reflect(T).kind é um enum (Struct(s), Enum(e), Slice(elem), Array(elem, n),
Optional(inner), Tuple(parts), Int(i), Float(f), Bool, String, Ptr…). s.fields com f.name,
f.type, f.get(v); construir com reflect(T).construct(fn(f) => ...); fail("msg") aborta a compilação.
Reflexão de tipo, não de valor em runtime (não existe any).
Makoto
use compiler.{reflect, fail}fn count_fields[T](v: T) -> int { var n := 0 comptime match reflect(T).kind { Struct(s) => { comptime loop f in s.fields { n += 1 } } _ => { } } return n}TypeScript
Object.keys(v).length // runtime; tipos somemAuto-derivação: serialize / deserialize / hash
Section titled “Auto-derivação: serialize / deserialize / hash”Status: implementado
Sem macro nem anotação: a stdlib encoding deriva pela reflexão. Struct vira chave de Map/Set e payload de
rede (Serializable) sem declarar nada. Tipo com invariante pode definir validate (checado no decode).
Makoto
use encoding.{serialize, deserialize, hash}decl User { id: u64; name: string; admin: bool }u := User{id: 42; name: "ana"; admin: true}bytes := serialize(u)back := deserialize[User](bytes) catch |e| { return }let h: u64 = hash(u)TypeScript
JSON.parse(s) as User // sem validaçãoGeradores (sequências preguiçosas)
Section titled “Geradores (sequências preguiçosas)”@generator fn + yield = máquina de estados stackless no mesmo processo (zero alocação por item). Pra
suspensão arbitrária entre chamadas, use um processo. Abandonar um gerador roda os defers dele.
@generator fn + yield Generator[T]
Section titled “@generator fn + yield Generator[T]”Status: implementado
yield só no nível do próprio gerador (não dentro de uma função que ele chama).
Makoto
@generator fn nat() -> int { var i := 0 loop { yield i; i += 1 }}loop n in nat().take(5) { io.println(n) }TypeScript
function* nat(): Generator<number> { let i = 0; for (;;) yield i++; }Pipeline preguiçoso: .filter .map .take .collect
Section titled “Pipeline preguiçoso: .filter .map .take .collect”Status: parcial
Cada estágio é outro Generator (sem alocação intermediária). Implementados: map, filter, take,
collect. Desenhados e ainda não implementados: drop, reduce, enumerate, zip.
Makoto
fn even(n: int) -> bool { return n % 2 == 0 }loop v in nat().filter(even).map(fn(n: int) => n * 10).take(3) { io.println(v) }xs := upto(4).collect()TypeScript
// idem (lib esnext)Contratos (DbC), assert/assume e unsafe
Section titled “Contratos (DbC), assert/assume e unsafe”Dois níveis de contenção: o contrato na fronteira da função (@requires/@ensures) e o abate da
operação perigosa no corpo (assert (cond) checa; assume "motivo" confia). unsafe não é região de permissão:
é uma dívida que alguém paga explicitamente.
Pré-condição @requires(cond)
Section titled “Pré-condição @requires(cond)”Status: implementado
Checada em cada chamada: estática onde prova, runtime (panic determinístico) onde não. Empilha (uma por linha; o compilador diz qual falhou). Predicado reutilizável = função booleana comum. Pré-condição checável torna a função segura.
Makoto
decl Buffer { data: []u8 }@requires(i < buf.data.len)@requires(i >= 0)fn at(buf: Buffer, i: int) -> u8 { return buf.data[i] }TypeScript
function at(buf: Uint8Array, i: number) { if (i >= buf.length) throw new RangeError(); return buf[i]; }Pós-condição: -> T == expr (terse) ou @ensures
Section titled “Pós-condição: -> T == expr (terse) ou @ensures”Status: implementado
Terse no tipo de retorno (retorno único, o tipo é o slot). @ensures(...) pra várias condições/retornos; quando
um tipo se repete, o retorno ganha nome (rótulo do contrato, não variável).
Makoto
fn at(buf: Buffer, i: int) -> u8 == buf.data[i] { return buf.data[i] }@ensures(lo < hi)fn split(a: int) -> (lo: int, hi: int) { return (a, a + 1) }TypeScript
assert (cond) e assert (cond) else return E
Section titled “assert (cond) e assert (cond) else return E”Status: implementado
Checa em runtime; falso = panic (troca UB por falha limpa). Com else return devolve um erro/valor em vez de panic.
Makoto
assert (x > 0)fn parse(v: int) -> int { assert (v >= 0) else return -1 return v}TypeScript
if (!(x > 0)) throw new Error("assert");unsafe (expr, bloco, fn, union) + handlers
Section titled “unsafe (expr, bloco, fn, union) + handlers”Status: implementado
Toda operação perigosa (deref cru, [*]T[i], union, chamada C, fn unsafe) exige um handler: assert (cond)
(checa), assume "motivo" (confia; motivo não-vazio e conhecido em compile-time) ou passar adiante
(unsafe fn). Função que abateu tudo no corpo é fn normal — o contágio para na primeira camada que prova.
Makoto
unsafe fn raw_at(xs: []int, i: int) -> int { return xs[i] }v := unsafe raw_at(xs, 1) assert (1 < xs.len)y := unsafe fb.bits assume "reinterpretação intencional"r := unsafe { a := raw_at(xs, 0) return a * 2} assume "índice 0 existe"TypeScript
Predicado comptime fn em contrato (prova em compile-time)
Section titled “Predicado comptime fn em contrato (prova em compile-time)”Status: implementado
Se os argumentos são conhecidos em compile-time, o predicado dobra: verdadeiro = contrato quitado sem custo;
falso = erro de compilação. Com argumento de runtime vira checagem normal. Transforma assume em assert provado.
Makoto
comptime fn same_size[A, B]() -> bool { return mem.size_of[A]() == mem.size_of[B]() }y: u32 = unsafe fb.bits assert (same_size[f32, u32]()) // provado pelo compiladorTypeScript
FFI com C (e C++)
Section titled “FFI com C (e C++)”@cimport("header.h") roda o clang embutido, traduz o header inteiro e cria o submódulo c.<basename>.
Tudo do C fica qualificado sob c. (inclusive tipos fundamentais, nomeados por ABI: c.systemv.int). Toda
chamada C é unsafe e precisa de handler. Toolchain hermético (musl embutido, mk cc = cc drop-in).
@cimport("x.h") c.x.*
Section titled “@cimport("x.h") c.x.*”Status: parcial
Funções, structs, typedefs, enums, macros objeto (literais) e função (via shim). as renomeia o submódulo.
Lacunas atuais (registradas): não há como passar -D (ex.: _GNU_SOURCE — some unshare/clone/CLONE_*
do musl); macros com expressão ((1<<18)) são descartadas; errno ainda não é exposto.
Makoto
@cimport("stdlib.h")n := unsafe c.stdlib.atol("100kg".to_cstring()) assume "string válida"TypeScript
// idemTipos C qualificados por ABI (c.systemv.int)
Section titled “Tipos C qualificados por ABI (c.systemv.int)”Status: implementado
Não existe c_int solto: a largura depende da psABI. c.systemv.long = 64 bits, c.win64.long = 32;
c.gcc.int/c.clang.int seguem o alvo. Nomes de um token só (unsigned long use u64).
Makoto
let n: c.systemv.int = 5sz: c.systemv.size_t = 16TypeScript
NULL Optional
Section titled “NULL Optional”Status: implementado
Ponteiro C que pode ser nulo chega como Optional[*...] e é aberto com match. char* chega cru (não vira
string sozinho).
Makoto
v := unsafe c.stdlib.getenv("HOME".to_cstring()) assume "getenv"match v { some(p) => io.println("definida"); none => io.println("ausente") }TypeScript
Struct para o C: @repr(c)
Section titled “Struct para o C: @repr(c)”Status: implementado
Suas structs são reordenadas; pra cruzar a fronteira use @repr(c) (ordem e padding do C). Structs vindas do
@cimport já são @repr(c). Enums C são int-compatíveis (aceitam |/&).
Makoto
@repr(c)decl Point { x: c.systemv.int; y: c.systemv.int }TypeScript
Memória do C: @mm(c), @promote, @pin/@unpin
Section titled “Memória do C: @mm(c), @promote, @pin/@unpin”Status: implementado
Memória que o C te dá fica sob @mm(c) (liberada com o free do C). @promote(gc) repatria pro seu GC.
Objeto @mm(gc) passado ao C exige @pin (senão erro de compilação); @unpin é responsabilidade sua.
Makoto
p: *u8 @mm(c) = (unsafe c.stdlib.malloc(16) assume "c").or_panic()mine := p @promote(gc)@pin head// ... C segura o ponteiro ...@unpin headTypeScript
@callback (função sua chamada pelo C)
Section titled “@callback (função sua chamada pelo C)”Status: implementado
Ganha ABI C pra ser passada como ponteiro de função. Corpo não aloca heap nem faz IO (não há processo na thread
crua do C); pode enviar em canal e fazer spawn (roteado pelo runtime).
Makoto
@cimport("stdlib.h")@callback fn bye() {}_ := unsafe c.stdlib.atexit(bye) assume "registrar handler de saída"TypeScript
@extern (exportar pro C / declarar símbolo)
Section titled “@extern (exportar pro C / declarar símbolo)”Status: implementado
Expõe função com ABI C e nome cru (sem mangling), sem genéricos. Também declara símbolo externo sem corpo.
mk build --emit=lib gera .a + header C; mk export cmake|ninja gera a cola de build.
Makoto
@extern fn my_library_init() -> i32 { return 0 }@extern fn puts(s: [*]u8) -> i32TypeScript
mem.reinterpret[T, V](p) (void* T*)
Section titled “mem.reinterpret[T, V](p) (void* T*)”Status: planejado
Primitiva unsafe dedicada pra reinterpretar ponteiro (ex.: comparador do qsort lendo void*). Desenhada,
ainda não implementada (reinterpret não é membro de mem).
Makoto
ia := unsafe mem.reinterpret[c.gcc.void, i32](a) assume "a aponta um i32" // planejadoTypeScript
Dependências C: cdeps no mk.project + mk cc
Section titled “Dependências C: cdeps no mk.project + mk cc”Status: implementado
O .c entra no build (estilo Zig). Libs C declaradas com versão e checksum (sem auto-descoberta de -l).
mk cc é um cc/c++ drop-in com o sysroot musl embutido.
Makoto
cdeps { sqlite3 3.45.0 sha256:...}// shell: mk cc -c shim.c -o shim.oTypeScript
// package.jsonAssembly inline
Section titled “Assembly inline”Fronteira mais baixa, sempre unsafe. Duas formas: função-asm (corpo inteiro em asm, params/retorno
ligados a registradores) e bloco inline (instruções no meio do código, com in/out/clobber). Dialeto
nativo de cada arquitetura (Intel/AT&T, ARM, RISC-V, WASM); seleção por arquitetura com comptime if.
Função-asm: @asm(x86, att) unsafe fn ...
Section titled “Função-asm: @asm(x86, att) unsafe fn ...”Status: implementado
Parâmetro e retorno ligados a registrador no próprio parâmetro. Clobbers implícitos (é fronteira de chamada).
Makoto
@asm(x86, att)unsafe fn mask(a: u64 @asm(in, rax)) -> u64 @asm(out, rax) { andq $255, %rax}r := unsafe mask(x) assume "x86_64"TypeScript
Bloco inline com in/out/clobber
Section titled “Bloco inline com in/out/clobber”Status: implementado
Decoradores empilhados acima do unsafe {}; operando pode ser local, campo ou elemento. Clobbers explícitos
(correção, não verbosidade). É statement, não expressão.
Makoto
var lo: u32 = 0var hi: u32 = 0@asm(x86, intel)@asm(out, rax) lo@asm(out, rdx) hi@asm(clobber, "memory")unsafe { rdtsc}TypeScript
Registrador escolhido pelo compilador: xreg
Section titled “Registrador escolhido pelo compilador: xreg”Status: implementado
@asm(in, xreg): o alocador escolhe; no corpo use o nome simbólico do operando.
Makoto
@asm(x86, intel)unsafe fn double(value: u32 @asm(in, xreg)) -> u32 @asm(out, xreg) { add value, value}TypeScript
Seleção por arquitetura + WASM
Section titled “Seleção por arquitetura + WASM”Status: implementado
use arch + comptime if arch.current == .x86_64 { ... }; asm da arquitetura errada não compila (sem fallback
mágico). WASM não tem registradores: liga pelos próprios parâmetros.
Makoto
use archfn timestamp() -> u64 { comptime if arch.current == .x86_64 { return unsafe rdtsc_x86() assume "rdtsc disponível" } return portable_clock()}@asm(wasm, wasm)unsafe fn add(a: u32, b: u32) -> u32 { local.get a local.get b i32.add}TypeScript
Módulos, visibilidade e pacotes
Section titled “Módulos, visibilidade e pacotes”use é a única palavra de import. Nada é “fundamental demais pra importar”: print vive em io, panic em
runtime. Visibilidade em 3 níveis com o meio como padrão. mk.project = binário/composição; mk.mod = módulo
(lib) com as próprias deps; mk.sum = checksums. Resolução MVS (estilo Go), sem ciclos.
use (módulo, seletivo, alias, inline)
Section titled “use (módulo, seletivo, alias, inline)”Status: implementado
use json (acesso qualificado), use strings.{to_upper} (traz nomes), use x as y, e use math.abs(-5.0)
inline (chamada avulsa sem poluir o escopo). Prefixo decide a origem: nome puro = stdlib; app/auth = local;
github.com/... = externo (precisa estar em deps).
Makoto
use iouse strings.{to_upper}use collections as colx := use math.abs(-5.0)TypeScript
import * as col from "./collections";Visibilidade: priv / (nada) / pub
Section titled “Visibilidade: priv / (nada) / pub”Status: implementado
priv fn = só o arquivo; sem marca = o módulo inteiro (padrão); pub fn = exportado. Campo de struct inverte o
padrão (privado; pub expõe). Capitalização não importa (convenção: snake_case valores, PascalCase tipos).
Makoto
priv fn helper() {}fn internal() {}pub fn api() {}TypeScript
export function api() {}mk.project, mk.mod, mk.sum
Section titled “mk.project, mk.mod, mk.sum”Status: implementado
Projeto (bin) declara versão da linguagem, deps, cdeps, tasks e strict-mm. Cada mk.mod é um módulo-lib
com as próprias deps. Pasta sem mk.mod funde no módulo pai. Import absoluto (nunca ../).
Makoto
package appmakoto 0.1.0deps { github.com/acme/jwt 1.4.0}[tasks.greet]run = echo hisubprocess = [echo]
// auth/mk.modmodule authTypeScript
// package.json + tsconfig.jsonMódulo de arquivo único (module Nome)
Section titled “Módulo de arquivo único (module Nome)”Status: parcial
Um arquivo transportável com module Nome no topo; o design prevê deps com versão e checksum embutidos no
cabeçalho (implementação parcial).
Makoto
module quickscriptdeps { github.com/foo/json 0.9.0 sha256:a1b2c3...}TypeScript
// idemSem imports circulares; init topológica
Section titled “Sem imports circulares; init topológica”Status: implementado
ABA é erro. Inicialização top-level é const (comptime) ou pura; efeito de boot vai num processo explícito.
Makoto
// a.mko: use app/b + b.mko: use app/a -> erro de compilaçãoTypeScript
// idemMétodos em tipo de outro módulo (órfãos)
Section titled “Métodos em tipo de outro módulo (órfãos)”Status: implementado
Pode definir fn (p: other.Point) norm(). Se dois módulos visíveis definem o mesmo, a chamada sem qualificar é
ambígua (erro) — qualifique (A.norm(p)).
Makoto
pub fn (p: geo.Point) norm() -> float { ... }TypeScript
declare module "./geo" { interface Point { norm(): number } }Observabilidade do runtime
Section titled “Observabilidade do runtime”Pull e desligado por padrão: você só paga pelo que liga. Introspecção (snapshot barato) e trace (stream de eventos num ring buffer). Telemetria de aplicação (métricas, spans, logs) é stdlib.
Introspecção: runtime.processes/process/group/channels/stats/self
Section titled “Introspecção: runtime.processes/process/group/channels/stats/self”Status: implementado
Handle de processo expõe nome, estado, memória por estratégia de @mm, restarts, supervisor/filhos…
Makoto
use runtimeprocs := runtime.processes()stats := runtime.stats()n := runtime.self().restarts()TypeScript
process.memoryUsage();Trace de eventos: runtime.trace(alvo, eventos, ...)
Section titled “Trace de eventos: runtime.trace(alvo, eventos, ...)”Status: implementado
Eventos .send .receive .spawn .exit .schedule_in .gc .restart .channel_block… Consumo via .buffer (padrão),
.channel, .callback(fn), .endpoint; opções sample: N, duration, inherit.
Makoto
t := runtime.trace(.all, .spawn)spawn worker()io.println(t.count())TypeScript
// idemTelemetria: metrics, tracing, log
Section titled “Telemetria: metrics, tracing, log”Status: implementado
Stdlib escrita em Makoto: metrics.counter/gauge/increment/read, tracing.span/set_attribute/finish,
log.debug/info/warn/error.
Makoto
use metricsuse tracinguse logc := metrics.counter("requests")s := tracing.span("http.request")log.info("worker started")TypeScript
console.info("worker started");Testes, benchmarks, property-based e fuzzing
Section titled “Testes, benchmarks, property-based e fuzzing”mk test roda @test/@bench; @property e @fuzz são da extensão de testes (--ext=test ou no
mk.project). Matriz de execução por arquivo com @testmode(ordem, estado).
@test fn + assert
Section titled “@test fn + assert”Status: implementado
Teste solto ao lado do código, ou arquivos x_test.mko / pasta tests/. Cada teste roda como processo.
Makoto
@test fn soma() { assert (1 + 1 == 2) }TypeScript
test("soma", () => { expect(1 + 1).toBe(2); });@bench fn
Section titled “@bench fn”Status: implementado
Estilo Go: esforço configurável, comparador estatístico fixo (anti p-hacking).
Makoto
@bench fn bench_spin() { spin(50) }TypeScript
bench("spin", () => spin(50));@testmode(ordem, estado) (matriz)
Section titled “@testmode(ordem, estado) (matriz)”Status: implementado
order: sequential | parallel; state: isolated | shared. Listas geram o produto cartesiano, com relatório por
combinação (acha bugs de transição e races em estado lógico/externo).
Makoto
@testmode(sequential, [isolated, shared])@test fn s_one() { assert (1 == 1) }TypeScript
// idem@property(runs: N) (property-based com shrinking)
Section titled “@property(runs: N) (property-based com shrinking)”Status: implementado
Argumentos gerados (gen), falha reduzida ao menor contraexemplo.
Makoto
use test@property(runs: 50) fn prop_commutes(a: int, b: int) { test.expect(a + b == b + a) }TypeScript
fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => a + b === b + a));@fuzz fn f(data: []u8) (guiado por cobertura)
Section titled “@fuzz fn f(data: []u8) (guiado por cobertura)”Status: implementado
Corpus e crashes persistidos; alvo recebe bytes arbitrários.
Makoto
use test@fuzz fn fuzz_utf8(data: []u8) { match string.from[u8](data) { Ok(s) => test.expect(s.bytes.len == data.len); Err(e) => { let _ := e } }}TypeScript
Extensão UI (.mkoui)
Section titled “Extensão UI (.mkoui)”Primeira extensão oficial do compilador (não é core): arquivo .mkoui liga a gramática de markup. Reatividade
fina estilo SolidJS (sem Virtual DOM), ilhas (só o reativo vira WASM/JS no cliente), SSR, CSS com escopo
verificado, @server/@client, @jsimport, e transpile pra JS (--target=js).
@component fn retornando markup
Section titled “@component fn retornando markup”Status: implementado
Componente = função; props = parâmetros; markup vai no return { ... }. {{ }} = conteúdo entre tags;
{ } = valor depois de = (prop, atributo dinâmico, handler).
Makoto
@componentfn Greeting(name: string, count: int) { return { <div class="card"><h1>Olá, {{name}}</h1><p>{{count}} mensagens</p></div> }}// uso: <Greeting name="Ana" count={5} />TypeScript
function Greeting(p: { name: string; count: number }) { return <div>...</div>; }@state, @derived, @effect, @context
Section titled “@state, @derived, @effect, @context”Status: implementado
@state = variável reativa; @derived = recomputada quando dependências mudam; @effect = roda quando
dependências mudam (só no cliente); @context = injeção pela árvore (consumidor sem provider = erro de compilação).
Makoto
@client@componentfn Counter() { @state n := 0 @derived doubled := n * 2 @effect { io.println("n = {{n}}") } return { <p>{{doubled}}</p> }}TypeScript
const [n, setN] = createSignal<number>(0);@component(layout) + slot
Section titled “@component(layout) + slot”Status: implementado
Layout é componente cujos parâmetros slot recebem o conteúdo filho (como children). @component(page) gera
documento HTML completo.
Makoto
@component(layout)fn Shell(body: slot) { return { <main>{{body}}</main> } }@componentfn Page() { return { <Shell><h1>Oi</h1></Shell> } }TypeScript
function Shell(p: { children: JSX.Element }) { return <main>{p.children}</main>; }@server / @client e ilhas
Section titled “@server / @client e ilhas”Status: implementado
Restrições, não marcas obrigatórias: @server nunca vai pro cliente (segredos, DB); @client só roda no
browser (vira ilha). Sem marca = roda onde for chamado. A restrição propaga (chamar @server de @client = erro).
Makoto
@serverfn load_count() -> int { return 7 }@componentfn Badge() { return { <span>{{load_count()}}</span> } }TypeScript
"use server";CSS com escopo, @scope(global), @css("arquivo.css")
Section titled “CSS com escopo, @scope(global), @css("arquivo.css")”Status: implementado
Classes do <style> do componente ganham hash (não colidem); class="X" sem .X definido = erro de compilação
(inclusive classes de CSS importado, ex. Tailwind). @scope(global) desliga o hash; class={expr} dinâmico.
Makoto
@componentfn Pill(active: bool) { return { <span class={ active ? "pill-on" : "pill" }>x</span><style>.pill { color: gray } .pill-on { color: green }</style> }}TypeScript
import styles from "./Pill.module.css";@jsimport("mod", browser|node) js.mod.*
Section titled “@jsimport("mod", browser|node) js.mod.*”Status: implementado
Análogo ao @cimport: tipos lidos de .d.ts, cola WASMJS gerada. Ambiente declarado propaga (usar dom em
código @server = erro de compilação, não window is not defined em runtime).
Makoto
@jsimport("dom", browser)el := js.dom.getElementById("app")TypeScript
const el = document.getElementById("app");loop dentro do markup
Section titled “loop dentro do markup”Status: implementado
O loop do core reaproveitado em posição de conteúdo; forma funcional equivalente com {{ xs.map(...) }}.
Makoto
@componentfn Lista(xs: []int) { return { <ul>loop x in xs { <li>{{x}}</li> }</ul> } }TypeScript
<ul>{xs.map((x: number) => <li>{x}</li>)}</ul>Camada de verificação (planejada)
Section titled “Camada de verificação (planejada)”Desenhada (§21 + verification.md), não implementada ainda (épico MKO-499). Ideia central: toda garantia é
uma obrigação sobre um predicado, descarregada no degrau mais alto possível — tipo (0) dobra comptime (1)
extensão SMT/model-checker (2) checagem runtime (3) aviso (4) assume humano (5). Nunca cai “pra fora da escada”.
Linear/afim no tipo: @must_consume / @consume_once
Section titled “Linear/afim no tipo: @must_consume / @consume_once”Status: planejado
Recurso que precisa ser consumido exatamente uma vez (transação) ou no máximo uma (buffer liberado). Typestate
via value-params: File[Open] File[Closed].
Makoto
decl Transaction @must_consume { conn: *Connection }fn close(f: File[Open] @consume) -> File[Closed] { ... }TypeScript
// n/a (branded types aproximam typestate)Tipos refinados: @where
Section titled “Tipos refinados: @where”Status: planejado
Tipo + predicado: alias Balance = i64 @where(i64 >= 0). Subtipagem por implicação; narrowing após if.
Makoto
alias Balance = i64 @where(i64 >= 0)alias NonEmpty[T] = (xs: List[T]) @where(xs.len > 0)TypeScript
type Balance = number & { __brand: "nonneg" }; // só nominal, sem provaCercas: @pure e @total
Section titled “Cercas: @pure e @total”Status: planejado
@pure: sem efeitos (alocação, spawn, FFI, IO, panic). @total: termina (recursão estrutural / medida
decrescente). Obrigatórias em comptime fn usada em posição de tipo.
Makoto
@pure @totalfn fact(n: int) -> int { return n <= 1 ? 1 : n * fact(n - 1) }TypeScript
Tipos de sessão: @protocol, @sends/@receives, dual
Section titled “Tipos de sessão: @protocol, @sends/@receives, dual”Status: planejado
Canal tipado por protocolo vira typestate linear que o checker percorre; dual P gera o lado espelhado.
Makoto
@protocol decl Auth { Credentials @sends @receives match { Ok => Token @receives Deny => Reason @receives }}let c: Channel[Auth] // o servidor recebe Channel[dual Auth]TypeScript
Tipos dependentes: camada prática e @proof
Section titled “Tipos dependentes: camada prática e @proof”Status: planejado
Prática: value-params (decl Vec[T, n: usize] { data: [n]T }) — os value-params já existem. Prova (@proof):
famílias indexadas/GADTs, normalização simbólica, Σ/Π; prova = programa total (Curry-Howard).
Makoto
decl Vec[T, n: usize] { data: [n]T }@proof decl Eq[A, x: A, y: A] -> prop { Refl: fn[a: A]() -> Eq[A, a, a] }TypeScript
Model checking: @spec (extensão) e táticas @by
Section titled “Model checking: @spec (extensão) e táticas @by”Status: planejado
@spec verifica propriedades temporais de um sistema de processos ancorado num @supervisor (safety/liveness).
@by { induction(xs); simp } (táticas) fica pro self-hosting.
Makoto
@spec(spec.always(spec.implies(submitted(req), spec.eventually(committed(req)))))TypeScript
Ferramentas (mk)
Section titled “Ferramentas (mk)”Um binário só. Compilador nativo: Go LLVM IR clang embutido; toolchain hermético (musl). Subcomandos atuais:
run, build, check, test, analyze, get, export, cc; mk sem argumentos abre o REPL.
mk run | build | check
Section titled “mk run | build | check”Status: implementado
-O0/-O2/--release, --target=<triple> (x86_64, aarch64, riscv64 via qemu, wasm32, js),
--emit=lib (estática + header C), flags de GC/scheduler/autofree.
Makoto
// mk run app.mko// mk build --release --target=aarch64-linux-musl app.mko// mk check app.mkoTypeScript
tsc && node dist/app.jsmk test, mk analyze, mk get, mk export, mk cc
Section titled “mk test, mk analyze, mk get, mk export, mk cc”Status: implementado
Testes; relatório do analisador de memória; baixar deps (Git, MVS); gerar cola CMake/Ninja; cc/c++ drop-in.
Makoto
// mk test// mk analyze app.mko// mk get github.com/acme/jwt// mk export cmake// mk cc -c shim.c -o shim.oTypeScript
npm testFormatter, LSP, linter, doc (///), migração, observer, debugger
Section titled “Formatter, LSP, linter, doc (///), migração, observer, debugger”Status: planejado
Desenhados (§22): compilador como processador de queries incremental (base do LSP), formatter sem config,
linter fechado, doc comments /// HTML/Markdown, ferramenta de migração, observer/debugger com breakpoints ligados.
Makoto
/// Soma dois inteiros.fn add(a: int, b: int) -> int { return a + b }TypeScript
prettier; eslint; tsserver