Pular para o conteúdo

Comparações

Makoto × JavaScript

makoto-vs-javascript.md · 3.951 linhas · 58 min de leitura

Mapeamento conceito a conceito da linguagem Makoto para JavaScript. Gerado de docs/mappings/_gen/ em 2026-10-05. Fonte de verdade do design: docs/english/*.md; sintaxe Makoto dos exemplos marcados verificada no compilador mk. 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 · TypeScript · Java · Rust · Stdlib · Índice

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.

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
}

JavaScript

function find(xs, t) {
const i = xs.indexOf(t);
return i === -1 ? undefined : i;
}

Diferença: JS tem null E undefined. Makoto tem só none, e ele não é um valor que ‘escapa’ pra qualquer tipo.

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 explicitamente

JavaScript

function parse(s) {
if (s === "") throw new Error("Empty");
return 42;
}

Diferença: Exceção JS é invisível na assinatura. Em Makoto o erro faz parte do tipo de retorno.

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())

JavaScript

class Point {
constructor(x, y) { this.x = x; this.y = y; }
sum() { return this.x + this.y; }
}

Diferença: Não há this: o receiver tem nome e tipo na assinatura.

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
}

JavaScript

async function handle(path) {
return await fs.promises.readFile(path, "utf8");
}

Diferença: async contamina quem chama. Em Makoto nenhuma assinatura muda.

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-time

JavaScript

// não há sobrecarga: um corpo com typeof

Diferença: JS decide em runtime com typeof; Makoto decide em compile-time.

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 compila
v := match n { 0 => return "zero"; _ => return "outro" }

JavaScript

const dbl = (x) => x * 2; // arrow de expressão retorna implícito

Diferença: Em Makoto isso só vale em lambda passado como argumento.

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 RHS
var xs: List[int] = List.new()

JavaScript

let total; // undefined

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).

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 := 5
let y: int = 10
x = 6 // ERRO: x não é var

JavaScript

const x = 5;

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 := 0
n = n + 1
n += 2

JavaScript

let n = 0; n += 3;

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 := 64
comptime fn fact(n: int) -> int { return n <= 1 ? 1 : n * fact(n - 1) }
const F := fact(5) // 120, calculado pelo compilador

JavaScript

const SIZE = 64; // só imutável, avaliado em runtime

Diferença: JS não tem compile-time.

Status: implementado

Largar um valor de retorno no chão sem _ := é erro: descartar é uma intenção e intenção se escreve.

Makoto

_ := compute()

JavaScript

compute();

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 := 1
var b := 2
a, b = b, a

JavaScript

const [q, r] = divmod(7, 2);
[a, b] = [b, a];

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 = 7
b: byte = 255
big: u128 = 1

JavaScript

const b = 255; // Number (f64)
const big = 1n; // BigInt

Diferença: JS não tem inteiros de largura fixa (salvo TypedArrays).

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 = 250
y := x +% 10 // 4 (embrulha)
z := x + 10 // PANIC em runtime: estouro de u8

JavaScript

(250 + 10) & 0xff // manual

Diferença: Number não estoura (vira float impreciso).

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 // 44
i := 3.9 as i32 // 3

JavaScript

const z = 300 & 0xff; const i = Math.trunc(3.9);

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.5
q: f128 = 1.0
z := 0.0 / 0.0 // ERRO em runtime (NaN proibido)

JavaScript

const z = 0 / 0; // NaN

Precisão arbitrária: BigInt, Decimal, BigFloat, BigFloatArb

Seção intitulada “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.10
b: Decimal = 0.20
io.println(a + b) // 0.30 exato
x: BigInt = 123456789012345678901234567890
io.println(x * x)

JavaScript

const x = 123456789012345678901234567890n;
// Decimal: sem nativo (decimal.js)

Diferença: BigInt nativo; decimal só via lib.

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 = 'é'

JavaScript

const k = 'w'.codePointAt(0);

Diferença: JS não tem tipo char; strings são UTF-16.

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 := 300ms
reply := ch <-> req timeout(5s) catch |e| { return }

JavaScript

const t = 300; // ms, por convenção

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)

JavaScript

const n = 4 * 1024;

Status: implementado

alias UUID = u8 é sinônimo transparente (intercambiável). Tipo distinto é um decl envolvendo o valor.

Makoto

alias UUID = u8
u: UUID = 7
io.println(u + 1)

JavaScript

// sem tipos

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) // Greater

JavaScript

const o = Math.sign(a - b);

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) }

JavaScript

// sem equivalente

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.

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") }

JavaScript

if (n > 0) {...} else if (n < 0) {...} else {...}

Status: implementado

Escolha entre dois valores. Três ou mais = match.

Makoto

icon := liked ? "♥" : "♡"

JavaScript

const icon = liked ? "♥" : "♡";

loop unificado (infinito / while / for-each / range)

Seção intitulada “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) }

JavaScript

for (;;) break;
while (n > 0) n--;
for (const x of xs) console.log(x);

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) }

JavaScript

for (let i = 0; i < 3; i++) console.log(i);

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 } }
}

JavaScript

outer: for (...) { for (...) { continue outer; } }

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 }

JavaScript

let x; for (;;) { x = 42; break; }

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()
}

JavaScript

switch (code) { case 200: case 201: case 204: ok(); break; default: other(); }

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 }
}

JavaScript

const v = n === 0 ? "zero" : "outro";

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
}

JavaScript

const f = open("cfg");
try { /* ... */ } finally { f.close(); }

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 runtime
if bad { runtime.panic("estado inválido") }

JavaScript

throw new Error("estado inválido");

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).

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}}") }

JavaScript

function add(a, b) { return a + b; }

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() // 10
f(1) // 2

JavaScript

function f(x = 5) { return x * 2; }

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()) } }

JavaScript

function sum(...xs) { return xs.reduce((a, b) => a + b, 0); }
sum(...arr);

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)

JavaScript

const double = (n) => n * 2; // proibido em Makoto

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 collections
ys := collections.map(xs, fn(n: int) => n * 10) // xs: List[int]
zs := collections.filter(xs, fn(n: int) { return n % 2 == 0 })

JavaScript

const ys = xs.map(n => n * 10);

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)
}

JavaScript

function outer(x) { function helper(v) { return v + 1; } return helper(x); }

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ícito

JavaScript

class Counter { get() { return this.n; } inc() { this.n++; } }

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()

JavaScript

class Stack { static new() { return new Stack(); } }

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)

JavaScript

// sem equivalente (typeof em runtime)

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"
}
}

JavaScript

typeof x === "boolean" ? "B" : "I"

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).

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}

JavaScript

const b = { x: 1.0, y: 2.0 };

Visibilidade de campo: privado por padrão, pub expõe

Seção intitulada “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
}

JavaScript

class User { #id; email; }

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)
}

JavaScript

const s = { kind: "circle", r: 2.0 };
switch (s.kind) { case "circle": ...; case "rect": ...; }

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 compilaria

JavaScript

// sem equivalente

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 = .Blue

JavaScript

paint("green");

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() }

JavaScript

// duck typing: qualquer objeto com .show()

Valor de interface (vtable) × bound genérico (monomorfização)

Seção intitulada “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 tipado
greet(p) // estático (monomorfizado)

JavaScript

const xs = [p, 3, "s"];

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)

JavaScript

function log(...xs) { xs.forEach(x => console.log(String(x))); }

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)

JavaScript

class P { toString() { return `P(${this.x})`; } }

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 promovido
take(b as A) // extração explícita

JavaScript

class B extends A {} // herança, não composiçã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] }

JavaScript

// objetos são referências: { val, next }

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 := t

JavaScript

const t = [1, "x"]; const [x, y] = t;

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"

JavaScript

new Uint32Array(new Float32Array([1.5]).buffer)[0]

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 }

JavaScript

// sem layout: DataView sobre ArrayBuffer

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 } // planejado
p := Perms.Read | Perms.Write

JavaScript

const READ = 1, WRITE = 2; const p = READ | WRITE;

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.

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)

JavaScript

const xs = [1, 2, 3];

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 := xs
ys[0] = 9 // xs continua [1, 2, 3]

JavaScript

const ys = [...xs]; // `ys = xs` compartilharia

Diferença: Em JS/TS/Java, atribuir array compartilha a referência.

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 inteiro
v[0] = 9 // xs agora é [9, 2, 3]
c := xs[0:2] // cópia de 2 elementos

JavaScript

const c = xs.slice(0, 2); // sempre cópia (view: TypedArray.subarray)

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))

JavaScript

const xs = []; xs.push(1); xs[9] ?? -1;

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 collections
var 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)

JavaScript

const m = new Map(); m.set("a", 1); m.get("a") ?? 0;
const s = new Set([3]);

Funções de alta ordem (collections.map/filter/reduce...)

Seção intitulada “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 collections
ys := 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)

JavaScript

const ys = xs.map(n => n * 10); const t = xs.reduce((a, n) => a + n, 0);

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()

JavaScript

const v = find(xs, 5) ?? -1;

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.

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 bytes
io.println(s.codepoints.len) // 5 codepoints

JavaScript

const s = "héllo"; // UTF-16

Diferença: s.length em JS conta unidades UTF-16 — nem bytes nem grafemas.

Acesso por unidade: .bytes[i], .chars[i], .codepoints[i]

Seção intitulada “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] // byte
v := s.bytes[0..2] // view por bytes

JavaScript

[...s][1] // por codepoint; grafema: Intl.Segmenter

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")

JavaScript

console.log(`Olá ${name}, ${2 + 3} itens`);

Comparação: operadores = bytes; .equals(_, .semantic) = Unicode

Seção intitulada “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) // NFC
use locale
c := locale.Collator.new(.en)
o := c.order("a", "b") // Ordering.Less

JavaScript

a === b; a.normalize() === b.normalize(); a.localeCompare(b, "pt");

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 + "!"

JavaScript

let s = "ab"; s += "c";

Conversões de encoding: to[u16], string.from[u16]

Seção intitulada “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 }

JavaScript

new TextEncoder().encode(s); // só UTF-8; UTF-16: manual

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") }

JavaScript

process.env.HOME

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 strings
parts := strings.split("a,b,c", ",")
s := strings.join(parts, "-")
u := strings.to_upper("abc")

JavaScript

"a,b,c".split(",").join("-"); "abc".toUpperCase();

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.

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)
}

JavaScript

function risky(n) { if (n < 0) throw new Error("Neg"); return n; }

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")
}

JavaScript

try { use(load(p)); } catch (e) { if (e.code === "ENOENT") ... }

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 } // propaga
w := risky(x) catch |e| { runtime.panic("falhou") } // aborta

JavaScript

let v; try { v = risky(x); } catch (e) { throw e; }

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
}

JavaScript

try {...} catch (e) { switch (e.message) {...} }

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)
}

JavaScript

// sem equivalente

Sucesso sem valor: retornar só o conjunto de erro

Seção intitulada “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") }

JavaScript

function sendIt(ok) { if (!ok) throw new Error("Full"); }

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") }

JavaScript

catch (e) { /* qualquer coisa */ }

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) }

JavaScript

// sem equivalente

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)}] // planejado

JavaScript

throw Object.assign(new Error("PortError"), { port: n });

Memória: gerenciamento sem cor, ponteiros, ownership

Seção intitulada “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.

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 automaticamente

JavaScript

const xs = [1, 2, 3]; // GC

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)
}

JavaScript

// sem gerenciamento manual (ArrayBuffer é GC)

Gate local de ownership (leak/double-free/UAF = erro de compilação)

Seção intitulada “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 path

JavaScript

n/a

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 mm
fn handle() {
@mm(arena) {
let a: [*]int = mm.alloc(8)
io.println(mm.used(a))
} // tudo liberado aqui
}

JavaScript

n/a

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() { ... }

JavaScript

n/a

@consume: parâmetro linear (ownership atravessa a função)

Seção intitulada “@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) }

JavaScript

n/a

@transfer: mover entre processos (invalida a origem)

Seção intitulada “@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 @transfer

JavaScript

worker.postMessage(buf, [buf]); // transferable: buf fica detached

@promote(mm, shallow|deep): copiar pra outro manager

Seção intitulada “@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 C
chan <- result @promote(gc) @transfer

JavaScript

structuredClone(obj)

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") }

JavaScript

new Worker(f, { resourceLimits: { maxOldGenerationSizeMb: 16 } })

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 = 5
decl Node { val: int; next: Optional[*Node] }

JavaScript

// sem ponteiros (referências a objetos)

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"

JavaScript

new Int32Array(buf, 4) // view com offset

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 := 5
bump(mut x) // x == 6

JavaScript

// primitivos só por valor: retorne o novo valor

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.mko

JavaScript

n/a

Analisador de memória consultivo (mk analyze, -Wmm, --strict-mm)

Seção intitulada “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)

JavaScript

n/a

Opções do coletor (--gc-roots, --gc-collector, --gc=wasmgc)

Seção intitulada “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.mko

JavaScript

// GC do engine

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.

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)

JavaScript

new Worker("worker.js").postMessage(data);

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)

JavaScript

const { port1, port2 } = new MessageChannel();

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}}"
}

JavaScript

// sem tipos

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 <- 42
v := -> ch catch |e| { return }
loop {
x := -> ch catch |e| { break } // produtor morreu: fim
process(x)
}

JavaScript

port.postMessage(42);
port.onmessage = (e) => use(e.data);

Request-reply síncrono <-> com timeout obrigatório

Seção intitulada “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 }

JavaScript

await Promise.race([request(port, 21), timeout(1000)]);

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")
}

JavaScript

await Promise.race([next(a), next(b), sleep(100)]);

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") }

JavaScript

const workers = new Map(); workers.set("db", w);

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.mko

JavaScript

// event loop single-thread

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 }
}

JavaScript

Atomics.add(sharedArray, 0, d); // SharedArrayBuffer

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.

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") }

JavaScript

worker.on("exit", (code) => {...}); worker.on("error", ...);

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")
}

JavaScript

worker.on("exit", (code) => code === 0 ? normal() : crashed());

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()
}

JavaScript

// pm2 / cluster (nível de processo do SO)

Estratégias: one_for_one / one_for_all / rest_for_one

Seção intitulada “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| { ... }

JavaScript

// manual

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() }

JavaScript

// manual

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.

Status: implementado

Cada conexão aceita vira um Channel[T]; trate cada cliente como um processo.

Makoto

use net
listener := net.listen("0.0.0.0:8080") catch |e| { return }
loop {
conn := net.accept[Msg](listener) catch |e| { continue }
spawn handle(conn)
}

JavaScript

net.createServer((sock) => sock.on("data", (buf) => handle(JSON.parse(buf)))).listen(8080);

Status: implementado

Mesma API de canal: <-, ->, select, timeout.

Makoto

chan := net.connect[Msg]("127.0.0.1:8080") catch |e| { return }
chan <- request
reply := -> chan catch |e| { return }

JavaScript

const sock = net.connect(8080, "127.0.0.1"); sock.write(JSON.stringify(req));

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).

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ícito

JavaScript

function id(x) { return x; }

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}

JavaScript

const b = { v: 5 };

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() }

JavaScript

// duck typing

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() }

JavaScript

n/a

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 = 4

JavaScript

n/a

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) }

JavaScript

n/a

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ário

JavaScript

n/a

comptime if / comptime match / comptime loop / comptime {}

Seção intitulada “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 arch
comptime if arch.current == .x86_64 { io.println("x86") }

JavaScript

// n/a (bundler define/DCE)

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
}

JavaScript

Object.keys(v).length // runtime

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)

JavaScript

JSON.stringify(u); JSON.parse(s);

@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.

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) }

JavaScript

function* nat() { let i = 0; for (;;) yield i++; }

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()

JavaScript

// iterator helpers (ES2025): nat().filter(even).map(n => n*10).take(3)

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.

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] }

JavaScript

function at(buf, i) { console.assert(i < buf.length); return buf[i]; }

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) }

JavaScript

n/a

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
}

JavaScript

console.assert(x > 0); // só loga

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"

JavaScript

n/a

Predicado comptime fn em contrato (prova em compile-time)

Seção intitulada “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 compilador

JavaScript

n/a

@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).

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"

JavaScript

// N-API / ffi-napi / WebAssembly

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 = 5
sz: c.systemv.size_t = 16

JavaScript

n/a

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") }

JavaScript

n/a

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 }

JavaScript

n/a

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 head

JavaScript

n/a

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"

JavaScript

n/a

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) -> i32

JavaScript

n/a

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" // planejado

JavaScript

n/a

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

mk.project
cdeps {
sqlite3 3.45.0 sha256:...
}
// shell: mk cc -c shim.c -o shim.o

JavaScript

package.json

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.

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"

JavaScript

// n/a (WebAssembly)

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 = 0
var hi: u32 = 0
@asm(x86, intel)
@asm(out, rax) lo
@asm(out, rdx) hi
@asm(clobber, "memory")
unsafe {
rdtsc
}

JavaScript

n/a

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
}

JavaScript

n/a

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 arch
fn 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
}

JavaScript

n/a

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.

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 io
use strings.{to_upper}
use collections as col
x := use math.abs(-5.0)

JavaScript

import fs from "node:fs";
import { toUpper } from "./strings.js";

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() {}

JavaScript

export function api() {}

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

mk.project
package app
makoto 0.1.0
deps {
github.com/acme/jwt 1.4.0
}
[tasks.greet]
run = echo hi
subprocess = [echo]
// auth/mk.mod
module auth

JavaScript

// package.json + package-lock.json

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 quickscript
deps {
github.com/foo/json 0.9.0 sha256:a1b2c3...
}

JavaScript

// deno: import direto de URL com lockfile

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ção

JavaScript

// ESM permite ciclos (bindings ao vivo, TDZ)

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 { ... }

JavaScript

Point.prototype.norm = function () {...} // monkey patch global

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

Seção intitulada “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 runtime
procs := runtime.processes()
stats := runtime.stats()
n := runtime.self().restarts()

JavaScript

process.memoryUsage(); performance.now();

Trace de eventos: runtime.trace(alvo, eventos, ...)

Seção intitulada “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())

JavaScript

// diagnostics_channel / async_hooks

Status: implementado

Stdlib escrita em Makoto: metrics.counter/gauge/increment/read, tracing.span/set_attribute/finish, log.debug/info/warn/error.

Makoto

use metrics
use tracing
use log
c := metrics.counter("requests")
s := tracing.span("http.request")
log.info("worker started")

JavaScript

console.info("worker started"); // OpenTelemetry via lib

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).

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) }

JavaScript

test("soma", () => { expect(1 + 1).toBe(2); }); // jest/vitest

Status: implementado

Estilo Go: esforço configurável, comparador estatístico fixo (anti p-hacking).

Makoto

@bench fn bench_spin() { spin(50) }

JavaScript

bench("spin", () => spin(50)); // vitest bench / tinybench

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) }

JavaScript

// vitest: --sequence.concurrent / isolate

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) }

JavaScript

fc.assert(fc.property(fc.integer(), fc.integer(), (a, b) => a + b === b + a)); // fast-check

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 } }
}

JavaScript

jazzer.js

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).

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

@component
fn Greeting(name: string, count: int) {
return {
<div class="card"><h1>Olá, {{name}}</h1><p>{{count}} mensagens</p></div>
}
}
// uso: <Greeting name="Ana" count={5} />

JavaScript

function Greeting({ name, count }) { return <div className="card">...</div>; } // JSX

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
@component
fn Counter() {
@state n := 0
@derived doubled := n * 2
@effect { io.println("n = {{n}}") }
return { <p>{{doubled}}</p> }
}

JavaScript

const [n, setN] = createSignal(0); const doubled = () => n() * 2; createEffect(() => ...); // Solid

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> } }
@component
fn Page() { return { <Shell><h1>Oi</h1></Shell> } }

JavaScript

function Shell({ children }) { return <main>{children}</main>; }

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

@server
fn load_count() -> int { return 7 }
@component
fn Badge() { return { <span>{{load_count()}}</span> } }

JavaScript

"use server"; export async function loadCount() { return 7; } // React Server Components

CSS com escopo, @scope(global), @css("arquivo.css")

Seção intitulada “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

@component
fn Pill(active: bool) {
return { <span class={ active ? "pill-on" : "pill" }>x</span><style>.pill { color: gray } .pill-on { color: green }</style> }
}

JavaScript

import styles from "./Pill.module.css"; // CSS Modules

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")

JavaScript

const el = document.getElementById("app");

Status: implementado

O loop do core reaproveitado em posição de conteúdo; forma funcional equivalente com {{ xs.map(...) }}.

Makoto

@component
fn Lista(xs: []int) { return { <ul>loop x in xs { <li>{{x}}</li> }</ul> } }

JavaScript

<ul>{xs.map(x => <li>{x}</li>)}</ul>

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

Seção intitulada “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] { ... }

JavaScript

n/a

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)

JavaScript

n/a

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 @total
fn fact(n: int) -> int { return n <= 1 ? 1 : n * fact(n - 1) }

JavaScript

n/a

Tipos de sessão: @protocol, @sends/@receives, dual

Seção intitulada “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]

JavaScript

n/a

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] }

JavaScript

n/a

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)))))

JavaScript

n/a

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.

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.mko

JavaScript

node app.js

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.o

JavaScript

npm test; npm install

Formatter, LSP, linter, doc (///), migração, observer, debugger

Seção intitulada “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 }

JavaScript

prettier; eslint; typescript-language-server