Pular para o conteúdo
Hora local do proprietário em SP:
docs / projetos / yanawa / design da linguagem

Design da linguagem yanawa

O que está aceito, o que é explorado, o que está em aberto.

experimentalregistro de designcódigo-fonte ↗

Como ler esta página

O yanawa está documentado apenas como design. Cada afirmação abaixo é marcada pela origem no repositório de código, no commit 7fd082e:

  • Aceito: registrado em docs/decisions.md como decisão, com a ressalva de que ainda pode evoluir.
  • Exploratório: descrito nos demais documentos de design como direção possível ou preferida.
  • Em aberto: listado pelos documentos como não resolvido.

Nada aqui está implementado. Não existe compilador nem interpretador contra o qual conferir qualquer item.

Decisões aceitas

IdDecisãoExemplo
D001Tipagem estática forte, com inferência que nunca enfraquece as garantiaslet age = 27 é um int
D002Blocos por indentação, introduzidos por dois-pontosif active:
D003Sem ponto e vírgula obrigatóriolet name = "Alex"
D004let declara uma variável mutávellet score = 0
D005const declara uma constante imutável; const let é evitadoconst MAX_RETRIES = 3
D006O tipo escrito vem depois do nomelet name: str = "Morgan"
D007O tipo é inferido quando óbviolet active = true
D008Tipos comuns não aceitam nulo; a ausência é explícitalet nickname: str? = none
D009A igualdade comum usa ==if name == "Riley":
D010Os operadores lógicos são as palavras and, or, notif not banned:
D011Strings interpolam expressões com chaves"Hello, {name}"
D012Funções usam fn, com o tipo de retorno depois de ->fn add(a: int, b: int) -> int:
D013Condições dispensam parênteses ao redorwhile running:
D014struct declara dados estruturados simplesstruct User:
D015Prefere-se a sintaxe familiar à novidadesem exemplo
D016A clareza vale mais que o número de caracteressem exemplo

Juntas, essas decisões dão o formato da linguagem: uma sintaxe pequena, baseada em indentação, em que mutabilidade, nulidade e tipos ficam visíveis onde importam.

Direções exploratórias

Estas aparecem nos documentos de design como direções possíveis ou preferidas, não como decisões.

Tipos, nulidade e resultados

Os documentos de tipos preferem inferência local, um conjunto de primitivos ainda não final (str, int, float, bool e char são candidatos) e str e str? como tipos diferentes. Propõem-se um operador de valor padrão ?? e um tipo result<T, E> com os construtores ok e error. A grafia exata, a análise de fluxo para valores anuláveis e as regras de conversão numérica estão em aberto.

fn divide(a: int, b: int) -> result<int, str>:    if b == 0:        return error("Division by zero")    return ok(a / b)

Structs, enums e traits

Structs são a construção principal de dados. A criação com campos nomeados, métodos declarados dentro do struct, composição em vez de herança, e trait com um impl separado são a direção preferida. Enums para variantes fechadas, possivelmente com valores associados, estão em consideração.

struct User:    id: int    name: str    active: bool = truetrait Printable:    fn display() -> str

Tratamento de erros

Falhas esperadas devem ser valores tipados, tratados com correspondência de padrões, tendo ? como possível operador de propagação. Ausência (valores anuláveis) e falha (resultados) ficam separadas. Asserções expressam suposições de quem programa, o pânico é reservado a estados irrecuperáveis, e exceções não são o modelo padrão.

Fluxo de controle

Condições usam if, elif e else. Laços percorrem coleções e intervalos, com while para condições. Um switch pode produzir um valor, e propõe-se um match separado para padrões. Se os dois devem existir é algo indefinido.

Concorrência, memória e módulos

async e await com concorrência estruturada são a direção preferida: tarefas filhas terminam com o escopo do pai, o cancelamento se propaga, e trabalho desacoplado exige intenção explícita. O código comum deve ser seguro quanto à memória sem alocação manual, mas a estratégia (coleta de lixo, posse, contagem de referências ou um modelo híbrido) está deliberadamente indefinida. Os módulos são orientados a arquivos, com caminhos de módulo estáveis em vez de longos imports relativos, e fn main() é o candidato a ponto de entrada.

Biblioteca padrão e pacotes

A biblioteca padrão deve continuar pequena e coerente, organizada em módulos importáveis como yanawa.fs, yanawa.http, yanawa.json e yanawa.time. O candidato a sistema de pacotes é um único manifesto, yanawa.toml, com um arquivo yanawa.lock, dependências por caminho local e versionamento semântico. Um registro não é necessário para um primeiro compilador.

Direção do compilador e das ferramentas

O documento de arquitetura do compilador propõe etapas separadas, com fronteiras claras:

source code  -> lexer -> tokens  -> parser -> syntax tree  -> name resolution  -> type checking  -> semantic representation  -> lowering -> intermediate representation  -> code generation -> target output

O mesmo documento diz que nem toda etapa precisa ser um subsistema separado, e que a linguagem de implementação, o alvo de execução, o runtime e as representações intermediárias estão indefinidos. O documento de ferramentas descreve um único comando yanawa com subcomandos como run, build, check, test, fmt, lint, add, remove e docs. O README afirma que eles descrevem intenção e que nem todos estão implementados, e o repositório não contém implementação de nenhum deles.

Perguntas em aberto

Os documentos deixam sem resolução, entre outras:

  • Retorno explícito ou implícito da expressão final.
  • O conjunto final de tipos primitivos e as regras de conversão numérica.
  • Mutabilidade de structs, regras de self e o alcance de private.
  • Sintaxe e restrições de genéricos.
  • A semântica exata de resultados e propagação.
  • A estratégia de memória e o modelo de runtime.
  • A linguagem de implementação do compilador e os alvos de compilação.
  • Quanto de HTTP, JSON, rotas e configuração pertence à linguagem, à biblioteca ou às ferramentas.

Referências