Pular para o conteúdo

Especificação §28

Referência

language-design.md §28 · 75 linhas · 6 min de leitura

O inventário canônico da linguagem: keywords, tipos builtin e decoradores. Serve a dois propósitos: ser a lista única do que mora no escopo global (o não-envenenamento da seção 1 em forma de tabela), e responder à pergunta que a contagem crua de decoradores provoca, “não são decoradores demais?”.

As palavras tecidas na gramática. São o escopo global irredutível: reservadas em todo programa, e por isso mantidas no mínimo.

  • Declaração: fn let var mut const decl alias union module use
  • Controle de fluxo: if else match loop in break continue return defer catch yield
  • Concorrência: spawn timeout
  • Compilação e segurança: comptime unsafe assert assume
  • Visibilidade: pub priv
  • Conversão: as
  • Literais: true false

Trinta e dois no total. O que parece keyword mas não é (e por quê):

  • type: não é keyword, é o kind (o tipo cujos valores são tipos, builtin, seção 10), e mora na lista de tipos builtin. Quem declara um tipo é decl; type é o que você escreve quando algo é um tipo (T: type, -> type). Não existe type Nome = …: estrutura nova é decl, apelido é alias (seção 2).
  • select: não existe; é o match sem sujeito (seção 4). Um construto a menos para o mesmo poder.
  • self: não existe; o receiver de um método tem nome livre (fn (d: *Dog) ...), e a auto-referência de um processo é o método runtime.self() (seção 3). Nenhum nome mágico reservado.
  • null: não existe; ausência é Optional[T] (seção 6), e o NULL do C vira Optional na fronteira (seção 17).
  • and / or / not: a linguagem usa os símbolos && / || / !.
  • generator: é o decorador @generator (seção 11), não keyword.
  • print / panic: não são keywords nem builtins; vivem em io / runtime e se importam (seção 1). O escopo global não carrega funções.

Os tipos que a sintaxe core pressupõe, disponíveis sem import porque a gramática os menciona (um Result no catch, um Channel no <-, um int num literal). É a lista fechada do que mora no escopo global; todo o resto é stdlib importada.

  • Resultado e ausência: Result[T, E] (variantes Ok/Err), Optional[T], e os conjuntos de erro error{...} (seção 7)
  • Concorrência: Channel[T] (seção 4)
  • Numéricos: a família de largura arbitrária iN/uN (1 a 65535) e os apelidos int/uint/isize/usize/byte/bit (byte=u8, bit=u1); a família de ponto flutuante f16/bf16/f32/f64/f128 (apelido float); bool; noreturn (o tipo bottom da divergência) (seção 14)
  • Texto: string, e as unidades de iteração byte/codepoint/grapheme, com char como apelido de grapheme (seção 16)
  • Agregados: [N]T (array de tamanho fixo) e []T (slice)
  • Ponteiros: *T (ponteiro-de-um) e [*]T (ponteiro-de-muitos, N contíguos que se possui, base de coleções, seção 6); os handles de alocação Ptr/RawPtr da camada de memória (seção 5)
  • Comptime: type e o universo de proposições prop (tipos e proposições são valores em comptime, seções 10 e 21); Generator[T] (seção 11); e o operador-de-tipo dual, que espelha um protocolo de sessão (seção 21)
  • Tempo: Duration (o u64 de nanossegundos do timeout, seção 14)

O que não é builtin, apesar de parecer fundamental: as coleções List/Map/Set (pacote collections), as interfaces Display/Serializable/Iterator/Writeable (fmt/encoding/io), os gerenciadores MemoryManager/MemorySource (pacote mem), as durações de precisão arbitrária Duration[T]/ArbitraryDuration (time), as máquinas de prova e tática (Eq/Refl e a library Goal/Stack/tática da seção 21, tudo Makoto sobre as primitivas de prova), e toda função (print em io, panic em runtime, reflect/fail em compiler, size_of/align_of/offset_of em mem). A linha é deliberada: builtin é só o que a sintaxe não consegue expressar sem; o resto se importa, e quem não usa não paga.

Keyword é conceito do core; decorador é etiqueta de subsistema. A divisão entre as duas categorias não é estética, é o que cada uma significa. Uma keyword é um conceito do núcleo, um ato que todo programa exerce (definir, comutar, repetir, declarar): o vocabulário comum. Um decorador é a etiqueta do subsistema a que algo pertence: @mm diz “isto é memória”, @supervisor diz “isto é concorrência”, @cimport diz “isto é FFI”. É por isso que a contagem crua assusta e não devia: você nunca encara os quarenta de uma vez, você encara os do subsistema em que está mexendo. O decorador não é ruído de sintaxe, é o carimbo de fronteira que diz em que sistema você entrou (seção 1).

A contagem crua assusta, são quarenta. Mas a contagem crua é a métrica errada, porque decoradores se agrupam por domínio, e um programa toca um ou dois domínios, nunca os quarenta. O que pesa não é o total no manual, e sim quantos você encontra na tarefa à frente. Agrupados:

  • Memória (6): @mm @transfer @promote @pin @unpin @limit. Alocador, posse e movimento (seção 5). É opt-in: com o GC default, você não escreve nenhum, porque são a superfície de quem desce para @mm(none)/arena.
  • Contratos (2): @requires @ensures. Pré e pós-condições verificadas (seção 5). Aparecem só onde você quer a checagem.
  • Concorrência e resiliência (2): @supervisor (árvores de supervisão, seção 8), @register (registro nomeado de processos, único, ou em grupo via @register(name, group), seção 3).
  • Composição (1): @embeds. Promoção de métodos e campos (seção 2).
  • FFI com C (4): @cimport @extern @callback @repr. A fronteira com C (seção 17). Um programa puro nunca os digita.
  • Assembly (1): @asm. Arquitetura e dialeto na função (@asm(x86, intel)) e binding de registrador no param ou local (@asm(in, rax)); asm inline (seção 18). Nicho do nicho.
  • Reatividade de UI (4): @state @derived @effect @context. Estado reativo (seção 20). Só existem em .mkoui, a extensão que um .mko comum não ativa.
  • Estrutura de UI (6): @component @server @client @jsimport @css @scope. Componentes, fronteira servidor/cliente, estilo escopado (seção 20). Também só em .mkoui.
  • Testes (3): @test @testmode @bench. Teste, mocking e benchmark (seção 1).
  • Generators (1): @generator. Funções que produzem por yield (seção 11).
  • Disciplina de posse (2): @must_consume @consume_once. Linearidade e afinidade nível-de-tipo (seção 21). Opt-in: só um recurso que você quer que o compilador rastreie exatamente-uma-vez/no-máximo-uma-vez os carrega.
  • Refinamento (1): @where. Um predicado num tipo (seção 21). Aparece só nos tipos que você restringe.
  • Cercas (2): @pure @total. Promessas de efeito e terminação (seção 21). Inferidas por default; você as escreve onde demanda a checagem, ou onde uma função comptime entra num tipo.
  • Sessões (3): @protocol @sends @receives. O protocolo de um canal e seus passos (seção 21). Só um Channel tipado por um @protocol os vê; um canal comum nunca.
  • Prova (1): @proof. A cerca que torna uma família indexada ou função em lógica confiável (seção 21). Nicho do nicho: só um kernel provado o carrega.
  • Model checking (1): @spec. Safety e liveness de um design de processos (seção 21); uma extensão, como as de UI. Mora ao lado do @supervisor, e um programa sem ele nunca o vê.

O agrupamento é a resposta à “morte por mil decoradores”: eles não chovem juntos. Um serviço de rede em GC toca talvez @supervisor e @register, dois. Quem desce para memória manual ganha os seis de memória, e nada de UI. Quem escreve UI ganha os dez de .mkoui, e nenhum de asm. As quatro portas de C e a porta de asm a maioria nunca abre. A densidade que você sente é a do seu domínio, e cada domínio tem uma conta pequena. É o opt-in (seção 1) aplicado à anotação: a sofisticação fica disponível, agrupada e fora do caminho até você convocá-la.


Anotações de implementação, não de design: escolhas de como construir, não de o que a linguagem é.

  • Frontend em Go. O lexer, tokenizer e parser provavelmente em Go, o que abre uma porta concreta: usar o TypeScript Compiler que a Microsoft reescreveu em Go pra ler os .d.ts do @jsimport (seção 20), em vez de reimplementar o type-checker do TS do zero.
  • Backend LLVM agora, próprio depois (seção 18), pragmático pros múltiplos alvos e o cross-compile.
  • Calling convention própria depois (seção 23): começamos herdando a do LLVM/C (interop grátis, sem duas convenções a manter); uma convenção interna otimizada vem junto do backend próprio.