- Um registro da análise do código C de 50 linhas do interpretador da linguagem K criado por Arthur Whitney, interpretando seu estilo de programação singular
- O código inclui várias estruturas experimentais incomuns em relação ao C tradicional, como sintaxe compactada baseada em macros, extensões não padrão de C e uso implícito de argumentos
- O autor interpreta diretamente o significado de cada macro e função, explorando a filosofia das linguagens da família APL e os prós e contras da densidade de código
- Como vantagens do código, aponta-se a curta extensão e alta capacidade de composição; como desvantagens, a sintaxe não padrão e a perda de legibilidade
- Em conclusão, esse código é avaliado como um exemplo que mostra não tanto como escrever de forma curta, mas sim a importância de uma forma de pensar que escreve o código somente depois de compreender completamente o problema
Arthur Whitney e seu código
- Arthur Whitney é um cientista da computação que projetou as linguagens A, K, Q e os bancos de dados kdb, Shakti
- kdb é um banco de dados de séries temporais ultrarrápido usado no setor financeiro, e Shakti é uma versão ainda mais rápida, projetada para processar conjuntos de dados na escala de 1 trilhão de linhas
- Suas linguagens são linguagens baseadas em arrays fortemente influenciadas por APL, priorizando concisão e expressividade matemática
- O foco do texto não está nas aplicações financeiras, mas em analisar o estilo singular do código C escrito por Whitney
A estrutura do interpretador K de 50 linhas
- O repositório público ksimple inclui um interpretador em C de cerca de 50 linhas que Whitney escreveu em poucos dias
- O núcleo do código é composto por dois arquivos,
a.h e a.c, e se caracteriza pela abreviação de definições de função por meio de macros e por uma estrutura que usa ponteiros como se fossem inteiros
- A instrução
typedef char*s,c; define s como ponteiro para string e c como tipo caractere
s Q=(s)128; é um exemplo de uso de ponteiro como inteiro; ao longo de todo o código, Q é usado como um valor especial que representa estado de erro
- São usadas várias extensões de sintaxe do GCC, como statement expression na forma
({e;}) e o operador ?:
O significado das principais macros e funções
#define _(e...) ({e;}) : macro que agrupa várias instruções em uma única expressão
#define i(n,e) : expressão abreviada de repetição, representando um loop for em uma linha
#define Q(e) e afins são macros de tratamento de erro; Qr, Qd, Qz retornam erros de rank, domain e not-yet-implemented, respectivamente
- As macros
_s, _i, f, F simplificam declarações de função e usam implicitamente os argumentos x e a
ax, ix, nx etc. são macros de verificação de tipo de dados e indexação; ax determina se “x é um átomo (atom)”
f(w,write(1,ax?&x:x,ax?1:strlen(x));x) é a função de saída: se for átomo, imprime como caractere; se for vetor, como string
Como o interpretador funciona
- A função
m(x) faz alocação de memória e gera um ponteiro com informação de comprimento incluída; o comprimento máximo do vetor é 255 bytes
- A macro
g(a,v) faz o tratamento unificado de operações sobre átomo/vetor, e funções como not, sub, At, _A são definidas com base nela
- A macro
G(f,o) faz a geração automática de funções de operadores binários, suportando operações como <, ==, +, *, &, |
cat, rev, cnt, Tak etc. são funções de manipulação de vetores; rev gera índices em ordem inversa usando a função ind
- A função
e() é um avaliador recursivo que lê a string da direita para a esquerda e processa variáveis de um único caractere, números e operadores
main() recebe entrada, avalia com e() e imprime o resultado em formato de laço REPL
Avaliação do estilo de código
- Vantagens
- Conjunto conciso de operações primitivas composto por macros combináveis
- Como o código é curto, é possível entender toda a lógica de uma vez, sem rolagem
- A expressão de alta densidade comprime a estrutura lógica do código
- Desvantagens
- Tratamento de tipos sem significado semântico, como usar
char* como se fosse inteiro
- Queda de legibilidade causada por uso direto de códigos ASCII, operadores ternários complexos e sintaxe não padrão
- O uso implícito de argumentos e os nomes curtos de variáveis dificultam entender a intenção
- Elementos neutros
- A sintaxe exclusiva do GCC (
?:, statement expression) é interessante, mas reduz a portabilidade
- O uso implícito de argumentos pode ser útil em código pequeno, mas em código grande pode causar confusão
- Nomes curtos podem ser eficientes depois que se ganha familiaridade, mas têm baixo poder de transmitir significado
Conclusão e lições
- Esse código mostra não simplesmente como escrever de forma curta, mas uma forma de pensar que escreve o código somente depois de compreender completamente o problema
- O código de Whitney é uma forma de transportar para o código um modelo matemático já concluído, ou seja, “um resultado de expressar o pensamento em código”
- O autor reflete sobre seu hábito usual de tentar resolver o problema dentro do código e,
daqui para frente, enfatiza a importância da modelagem conceitual e da organização do pensamento antes de escrever código
- Em última análise, o experimento é resumido como uma experiência de treinar a “capacidade de ler código” e explorar o equilíbrio entre densidade de código e clareza de pensamento
Ideias para experimentos futuros
- Propostas de prática para expandir o interpretador:
- Suporte a vetores de ponto flutuante
- Processamento de mais de 255 elementos
- Números com vários dígitos e nomes de variáveis
- Literais de array e ignorar espaços em branco
- Adição de gerenciamento de memória e exibição de erros
- Completar funções ainda não implementadas
- Essas extensões podem se tornar um experimento de desenvolvimento para transformar a linguagem em algo realmente utilizável, mantendo o estilo de código à la Whitney
1 comentários
Comentários no Hacker News
Os macros deste código servem para compactar operações comuns Eu li primeiro o J Incunabulum e depois vi este código, mas para programadores acostumados com C que começam a ler do meio, as definições de macro do início podem causar confusão Como os macros se constroem uns sobre os outros, o código sobe rapidamente a escada da abstração Gosto especialmente do macro
Iterate(i), que reduz loops verbosos a uma única letra O motivo de esse tipo de código denso ser difícil de ler é que ele cria dezenas de abstrações em poucas linhas e já as usa imediatamente Por isso é preciso ler devagar, uma letra de cada vez Para quem já trabalhou em grandes codebases compostas por centenas de arquivos finos, esse nível extremo de compactação até parece refrescantegrep *.[ch], sem subdiretórios Projetos Java em especial têm arquivos pequenos demais e com pouco conteúdo, então é difícil encontrar qualquer coisa Com uma IDE talvez fique melhor, mas eu não uso Whitney disse numa entrevista que queria colocar todo o código em uma página, e que sua “IDE” era o console do Windows e o NotepadPara entender o código C de Arthur Whitney, primeiro é preciso aprender uma linguagem da família APL Caso contrário ele vai parecer apenas um estilo estranho de C Whitney está tentando usar C como se fosse APL O estilo sem espaços, com nomes de uma letra e funções de uma linha é o mesmo do APL É parecido com um programador de Pascal usando algo como
#define begin {, mas Whitney é muito mais original do que isso#define begin {”, alguém brinca: “ah, tipo Stephen Bourne”Ao procurar por Shakti, vi que o link da Wikipedia redireciona para
k.nyc, e a página tem apenas uma letra: ‘k’ Olhando o fonte, era literalmente só `k` Isso parece uma versão em HTML do minimalismo ao estilo Whitney — remover tudo que é desnecessário e deixar o compilador cuidar implicitamente do resto
k
Este post de blog é uma excelente análise, independentemente da avaliação sobre o estilo de programação de Whitney Considerando que o autor o escreveu em 8 horas, há bastante profundidade, e a seção de conclusão foi especialmente marcante Link do original
Isso lembra a tentativa de Stephen Bourne de fazer C parecer Algol Dá para sentir uma vibe parecida vendo o exemplo em mac.h e o exemplo em expand.c
Toda área tem suas “best practices”, mas elas só funcionam bem para casos médios Em situações específicas, às vezes é melhor ir justamente na direção contrária No fim, a sabedoria coletiva serve como padrão inicial, mas quando você começa a pensar por conta própria, inevitavelmente passa a enxergar as brechas
Gostei de ver uma visão equilibrada, sem agressividade em relação ao código Foi divertido de ler e pretendo reler depois
Fiquei curioso se esse estilo de programação é um paradigma específico Quase nunca vi código assim em projetos reais, exceto coisas como “business card ray tracer” O código-fonte da linguagem J criada por Whitney também tem um estilo extremamente compactado parecido
Ao olhar para as seguintes definições de macro,