Notation como Ferramenta de Pensamento (1979)
(jsoftware.com)- A notação (Notation) é uma ferramenta importante para auxiliar o pensamento, desempenhando um papel central tanto na matemática quanto nas linguagens de programação
- A linguagem APL foi desenvolvida como uma tentativa de combinar as vantagens da notação matemática com a executabilidade e a universalidade de uma linguagem de programação
- As características de uma boa notação incluem concisão, clareza, sugestividade, subordinação dos detalhes e possibilidade de prova formal
- É possível representar e transformar com eficiência em APL várias estruturas matemáticas, como polinômios, transformações e grafos
- A introdução e o aprendizado da notação devem ocorrer naturalmente dentro do contexto, e a estrutura e a generalidade da notação também são importantes
Notação como Ferramenta de Pensamento
- Em áreas científicas como química e botânica, sistemas de nomenclatura também impulsionaram o progresso acadêmico
- George Boole enfatizou que a própria linguagem é um meio de pensamento
- A notação matemática é um exemplo representativo de linguagem que apoia o pensamento, reduzindo sua carga e ampliando a capacidade de raciocínio
- A.N. Whitehead e Charles Babbage destacaram a importância da notação matemática
O potencial das linguagens de programação como ferramenta de pensamento
- Linguagens de programação têm como pontos fortes a universalidade e a clareza
- Elas permitem experimentar ideias por meio do computador e realizar experimentos mentais de forma precisa
- No entanto, a maioria das linguagens de programação ainda é menos eficaz como ferramenta de pensamento do que a notação matemática
- O APL foi projetado como uma notação voltada a apoiar o pensamento, buscando clareza e precisão
Principais características de uma boa notação
- Facilidade de expressão do problema: deve permitir representar com facilidade estruturas derivadas diretamente do problema
- Sugestividade: a forma expressa deve sugerir problemas semelhantes ou expansões possíveis
- Subordinação dos detalhes: deve oferecer uma estrutura que simplifique detalhes complexos e favoreça o raciocínio
- Concisão: deve possibilitar ampla expressividade com o mínimo de símbolos e regras
- Possibilidade de prova formal: a notação deve ser adequada para provas formais e raciocínio dedutivo
Introdução às técnicas básicas de notação do APL
- Uso natural de estruturas baseadas em arrays, como vetores e matrizes
- Funções e operadores são aplicados automaticamente elemento a elemento sobre vetores/matrizes
- Operadores como redução(
/), scan(\) e produto interno(.) expressam composição de funções - Símbolos básicos como
⍳,⌽,⍴,+,×,*permitem compor expressões ricas - Todas as funções seguem a regra de precedência à direita, permitindo escrever expressões naturais sem parênteses
Exemplos de resolução de problemas e estímulo ao pensamento
- Sequências matemáticas como números triangulares e fatoriais podem ser expressas com fórmulas simples
- Representação de polinômios e operações como multiplicação e derivação são tratadas de forma concisa com regras consistentes
- Teoria dos grafos (árvores, fecho transitivo, árvores geradoras) também pode ser expressa com clareza por meio de operações sobre arrays
- Pode ser estendido a várias áreas, como permutações, álgebra booleana e conversão entre sistemas numéricos (fatoração em primos)
Prova formal e pensamento estruturado
- Como todas as operações e expressões são representadas em formas claramente executáveis, é possível fazer verificação automática com o computador
- São apresentados vários exemplos de prova formal por indução matemática, busca exaustiva e enumeração de identidades
- São formalmente demonstradas a partição (identity) de redução e scan, bem como associatividade e distributividade do produto interno
- São provadas diretamente funções simétricas de Newton, multiplicação de polinômios e fórmulas de derivação
Comparação entre APL e a notação matemática tradicional
- O APL oferece definição clara de funções, operações consistentes sobre arrays e um sistema rico de símbolos
- Em vez de regras de precedência para todas as operações, aplica-se a regra de execução da direita para a esquerda
- Reduz a complexidade do uso de símbolos matemáticos e apoia a manipulação formal (formal manipulation)
- A sintaxe é concisa e as regras são consistentes, beneficiando tanto iniciantes quanto usuários experientes
Introdução da notação e métodos de aprendizado
- Destaca-se a abordagem de introduzir naturalmente, no contexto, apenas a notação necessária, sem uma "aula de linguagem" separada
- Novos símbolos são aprendidos de forma intuitiva dentro de situações concretas de resolução de problemas
- Mais importante do que a dificuldade da notação em si é reconhecer as várias possibilidades e extensões que ela sugere
Possibilidades de expansão e propostas para o APL
- Propostas de expansão de funções, incluindo tratamento de números complexos
- Necessidade de padronizar funções de elementos únicos (unique elements) e resumo (summary)
- A introdução de operadores mais generalizados pode apoiar tópicos adicionais, como cálculo vetorial
- O objetivo é melhorar a clareza do design da linguagem e a capacidade de raciocínio
Equilíbrio entre eficiência e clareza
- Recomenda-se primeiro definir uma notação clara e analisável e depois aumentar a eficiência por meio de otimização
- Tornar o algoritmo mais claro também ajuda em otimizações posteriores e na otimização por compiladores
- Expressões básicas escritas em APL têm potencial para contribuir tanto para a investigação acadêmica quanto para aplicações industriais
1 comentários
Comentários do Hacker News
É fácil ver a notação como algo parecido com expansão de shell, no sentido de “substituir uma expressão por outra”, mas na prática ela é muito mais profunda
Um professor explicou que grandes descobertas muitas vezes aparecem junto com uma nova notação, e que uma nova notação significa “uma nova forma de pensar sobre este problema”
Acredito que muitos problemas em aberto hoje também poderiam ser resolvidos se surgisse uma notação poderosa
Isso é realmente poderoso, mas está mais próximo do estilo Lisp; já o estilo APL ou Clojure tende a tornar os tipos básicos realmente úteis
Em vez de ter 10 estruturas de dados com 10 funções para cada uma, é a abordagem de ter 1 estrutura de dados com 100 funções; então, em APL, mais do que criar DSLs, você projeta e organiza os dados com muito cuidado e o resto acaba se encaixando
Richard Feynman, quando era adolescente e aprendia trigonometria, não gostava da notação de seno e cosseno, então criou seus próprios símbolos matemáticos para simplificar as fórmulas e reduzir o ruído
Mais tarde, ele também recriou, ao mesmo tempo, formas de pensar e de representar a física, como nos diagramas de Feynman e na notação slash
Um pequeno exemplo: quando CoffeeScript apareceu, a abreviação de lambdas e várias conveniências sintáticas mudaram bastante a forma de escrever JavaScript, tornando mais fácil pensar, ler e corrigir
SML/Haskell e as famílias Lisp me dão uma sensação parecida
Você talvez também goste deste clipe curto de Brian Greene e Barry Mazur: https://youtu.be/8wQepGg8tHA
Historicamente, o que empurrou o APL para fora não foram apenas os teclados estranhos, mas também o Lotus 1-2-3, da IBM, e logo depois o MS Excel
Engenheiros, academia, contadores e MBAs precisavam de uma ferramenta melhor que a TI-59 ou a HP-12C, enquanto a área de ciência da computação estava mergulhada em processamento simbólico, IA e LISP; no fim, o mercado ocupou esse espaço
É uma coincidência lamentável, porque o APL poderia ter tido um impacto muito maior que as planilhas e resolvido muito mais problemas
A visão original era uma notação matemática consistente, executável e escrita à mão, mas isso nunca foi alcançado
Se você tiver interesse, vale a pena ler este texto: https://mlajtos.mu/posts/new-kind-of-paper
Você pode resolver problemas sem pagar, e o custo aparece quando distribui o resultado compilado para clientes pagantes
Se a solução se encaixar em um subconjunto específico, também dá para migrá-la para April e oferecê-la a partir de Common Lisp
Ainda assim, as pessoas do mundo APL em geral são muito acadêmicas; elas conseguem fazer trabalho de engenharia de forma rápida e concisa, mas, se você começar a falar de ranking de funções ou funtores naperianos em uma empresa média de software, seus colegas podem suspeitar que você precisa de ajuda médica
Uma parte considerável do desenvolvimento de software é inventar uma linguagem técnica, relativamente formal, que expresse a forma como clientes e usuários falam e pensam, e isso não é fácil nas linguagens da linhagem de Iverson
Por muito tempo, Java obrigou a explicitar quais palavras de negócio entram e saem de cada método e, nesse sentido, facilitou mapear os conceitos da organização para o código
Em APL, também é possível nomear dados e funções, mas, no momento em que você traz nomes longos e estruturas de namespaces para mapear uma organização externa no código, perde a concisão e a elegância
Mesmo nos sistemas de tipos sofisticados da família ML, desenvolvedores têm dificuldade de ligar diretamente ontologias semilinguísticas inventadas a organizações e processos, e com mais frequência escolhem conceitos matemáticos ou acadêmicos
Se houver alguém capaz de fazer as duas coisas, é possível, mas em geral basta ser bom apenas em traduzir para o mundo do cliente
No ano passado, o The Array Cast republicou uma entrevista de Iverson de 1982: https://www.arraycast.com/episodes/episode92-iverson
É bastante interessante e mais acessível que a palestra do Turing
O APL de 1979 não era uma linguagem estranha e periférica como é hoje
Na época, linguagens de programação ainda não eram um fenômeno de massa global como hoje, então quase todas eram meio estranhas e periféricas, e C também era bem recente
Com um pouco de boa vontade, APL parece uma abstração não muito distante de um C denso, permitindo programar o computador sem implementar diretamente manipulação de ponteiros sobre arrays
Há também vários livros didáticos que ensinam matemática com a sintaxe de APL [1] ou J [2]
Iverson originalmente usou o APL como uma sintaxe melhor para matemática, e a implementação de programação só veio alguns anos depois
[1] https://alexalejandre.com/about/#apl
[2] https://code.jsoftware.com/wiki/Books#Math_for_the_Layman
Uma vez ouvi um podcast de teoria dos tipos, hoje descontinuado, e era um conteúdo extremamente obscuro
O conceito básico está conectado a outros conceitos úteis
A hipótese Sapir-Whorf é parecida, mas fica mais interessante quando pensada ao contrário
Em uma linguagem imperfeita há coisas que não se consegue pensar, ou que são difíceis de pensar; então surge a pergunta se existem coisas que, com a linguagem que usamos, não conseguimos nem expressar nem pensar
Aqui, “linguagem” e “pensamento” podem ser entendidos de forma mais ampla do que o habitual
Por exemplo, as regras de interação social determinam a maneira como interagimos? Zeynep Tufekci diz em “Twitter and Teargas” que o Twitter torna flash mobs possíveis, mas dificulta mudanças sociais duradouras
Mecanismos sociais como seguir alguém, comentar ou dar like determinam ou possibilitam a forma como interagimos uns com os outros? Outros mecanismos poderiam possibilitar um pensamento coletivo melhor
Há também a música. Não a notação, mas será que a música expressa algo que não pode ser bem expresso de outras maneiras?
Como alguém que aprendeu várias línguas estrangeiras, há muitas coisas que só consigo pensar em certos idiomas e que são difíceis de pensar no inglês, minha língua materna
Por exemplo, o “гулять” em ucraniano e russo carrega muitos sentidos que o inglês não captura, e eu nunca havia pensado nesses sentidos antes de aprender essas línguas
“Гулять” significa literalmente “caminhar”, mas também é usado no sentido de sair em busca de experiências, inclusive experiências sexuais
Pode-se reclamar que alguém se casou cedo demais dizendo “не нагулялся”, isto é, “não caminhou o bastante”
Em inglês há expressões parecidas, como “sow his wild oats”, mas quando tantos sentidos cabem em um único verbo como “caminhar”, a própria ideia de caminhar pela vida muda
Quando aprendi árabe, também havia muitos significados e pensamentos que surgiam apenas nessa língua; não porque fossem impossíveis de explicar em inglês, mas porque não havia uma notação para expressá-los de forma concisa, então era preciso um texto longo
Algumas pessoas gostam de viajar para outros pensamentos por meio da linguagem, enquanto outras ficam paralisadas diante dessa possibilidade
Como sempre, o sucesso está no equilíbrio e em ambos os lados
Para matemáticos ou cientistas da computação isso é óbvio demais, mas a ideia é muito controversa entre linguistas e “educadores”
O análogo linguístico é a hipótese Sapir-Whorf, que afirma que a língua que se aprende determina a forma de pensar
Línguas naturais são objetos culturais, e mapear culturas, mesmo que como uma ordem parcial fraca, é visto quase como tabu no meio acadêmico
Isso também tem grandes consequências na educação, porque às vezes os alunos deixam de aprender a notação que lhes permitiria realmente raciocinar sobre os problemas que encontram
Sinceramente, não entendo bem essa parte
O fato de falantes de qualquer idioma poderem aprender a mesma matemática ou os mesmos programas de computador mostra isso
Também é questionável se a língua falada ou escrita é realmente necessária para o pensamento
No mínimo, existe uma grande área de pensamento possível sem linguagem, e os humanos já tiveram uma época sem fala, ou com muito pouca fala; pensamentos e intenções de comunicação criaram palavras e línguas
Por isso, parece estranho ver a língua aprendida como o modelo básico do pensamento
Por exemplo, se uma sociedade usa a mesma palavra para a cor do mar e a cor da grama, há quem afirme que ela não consegue experimentar a diferença entre as duas cores
Não é apenas que a experiência seja codificada na memória de maneira semelhante, mas sim que a diferença não é percebida
A afirmação de que sons inexistentes em uma língua simplesmente não podem ser ouvidos é parecida
A discussão sobre notação aqui está mais próxima da ideia de que o vocabulário pode ser usado para exploração
Como quando se passa a poder dizer não apenas que ouviu um som, mas que ouviu música, e ouviu determinada progressão de acordes etc.
No momento estou desenvolvendo um projeto em APL
Era algo que estava no backlog havia muito tempo, mas agora estou realmente escrevendo código
Houve um intervalo bem longo entre o momento em que me interessei por isso e o momento em que consegui escrever mais do que uma linha
No começo desse processo, descobri este artigo e o li quase como se o absorvesse; hoje esses conceitos se tornaram uma base completa do meu pensamento
Na prática, estou ensinando NAATOT em um programa de arquitetura
Não arquitetura de software, mas projeto arquitetônico
Uso uma versão editada para preservar o núcleo de Iverson, deixando apenas a matemática e a programação reais suficientes para mostrar o ponto central e desafiar os alunos a pensar de outra forma sobre as possibilidades de ferramentas de projeto e expressão
Ou seja, trata-se do processo de formar ideias e das maneiras de expressá-las para si mesmo e para os outros
Se eu tiver a oportunidade em um programa mais flexível e aberto, gostaria de oferecer uma disciplina em que os alunos criem seus próprios sistemas de símbolos e notação para aplicar ao campo do projeto arquitetônico
Lamento não ter terminado o app de anotações Freeform que eu estava criando antes
Era um app que compilava para páginas web independentes via SVG, e eu achava que era uma ideia realmente ótima para o tipo de conteúdo técnico comum nas áreas de STEM
Um exemplo antigo de anotações de química está aqui: https://colbyn.github.io/old-school-chem-notes/dev/chemistry-1010---fall-2021/week-14-acids-and-bases.html
Fico curioso para saber que sistema ele usa hoje
Durante anos, vi APL como uma espécie de magia, até que no começo deste ano tirei um tempo para aprender
Fiquei surpreso com a quantidade enorme de código que dá para colocar em um único tweet usando APL
É divertido, mas difícil de usar
Depois de escrever, quase sempre penso: “demorei tudo isso para escrever isto?” e fico me perguntando se deveria ter usado outra ferramenta
Mas, na prática, é contraintuitivo que outra ferramenta provavelmente teria levado muito mais tempo
A parte que parece difícil e consome tempo é, na verdade, o processo de ser forçado a organizar a especificação do problema de uma forma mais compacta
É parecido com subir por um caminho mais íngreme, mas muito mais curto: parece mais difícil, mas na realidade dá menos trabalho
Por isso acho que preciso aprender e usar APL
Pessoalmente, não concordo com a premissa do artigo de que “a notação matemática carece de universalidade e deve ser interpretada de maneiras diferentes conforme o tema, o autor e o contexto”
Acho alto o custo de uma notação separada da visualização do problema e da ergonomia humana
Alguns estudiosos preferem notações que escondem muita complexidade e podem produzir percepções do tipo “eureka” ou equivalências inesperadas, mas, em certos casos, elas acabam sendo turvas e propensas a erros
Ainda assim, é verdade que são uma ferramenta importante para comunicar processos de pensamento
Acho que ter apenas uma notação padrão para uma área, ou para áreas próximas, sufoca bastante os aspectos criativos, artísticos e exploratórios do raciocínio e da resolução de problemas
Há também uma ótima explicação de Terry Tao sobre notação: https://news.ycombinator.com/item?id=23911903
Na matemática, há esforços para criar sistemas de raciocínio “enterprise” como Lean e Coq, e nesses casos um sistema de notação universal faz sentido
Mas, para exploração pessoal, pode ser melhor encaixar qualquer coisa que funcione
Pessoalmente, tive mais dificuldade no ensino
Em aulas de álgebra e afins, era difícil quando o professor não tratava as decisões e preferências pessoais sobre notação de forma consistente ou honesta, e minha habilidade em matemática melhorou muito ao estudar teoria dos tipos e teoria de provas mecânicas
Acho que o conceito de “subordinação dos detalhes” do artigo não foi explorado a fundo o suficiente
Depois de passar muito tempo lendo e escrevendo aplicações em APL, percebi que esse conceito aponta para uma forma de gerenciar complexidade fundamentalmente diferente da abstração
Estamos cercados por barreiras de abstração como APIs, bibliotecas, módulos, pacotes e interfaces, e o resultado são problemas familiares como torres altas de abstração, desenvolvedores que apenas conectam APIs, desconexão do hardware e dificuldade de raciocinar sobre desempenho
APL torna muito confortável uma abordagem diferente
Em vez de projetar abstrações, projeta-se cuidadosamente os dados para que sejam fáceis de manipular com expressões simples
É como usar operações primitivas diretamente onde normalmente haveria uma função de biblioteca ou um termo de DSL
Por exemplo, com uma tabela de strings, um array de chaves e um array de valores, dá para criar uma estrutura parecida com um hashmap com valores vetoriais e chaves internalizadas, e lidar diretamente com inserção, saída e exclusão por meio de expressões APL
O ponto bom dessa abordagem é que cada expressão não é uma caixa-preta, então pode ser ajustada naturalmente a uma necessidade específica
Numa inserção em um hashmap comum, seria necessário código para adicionar uma nova chave, mas aqui aproveitamos a invariante comum de que basta acrescentar um valor a uma chave já existente
Com uma API de biblioteca, seriam necessários caminhos de código não usados, várias variantes de funções de inserção ou uma inferência de tipos sofisticada para eliminação de código morto
Essa abordagem deixa preocupações independentes do domínio vazarem para a base de código
Ao subordinar os detalhes em vez de escondê-los, é possível acessar os detalhes específicos do domínio na medida necessária, enquanto os detalhes irrelevantes permanecem quietos em segundo plano até serem necessários
Claro, é preciso estar muito familiarizado com expressões APL, mas não acho que isso seja um fardo muito maior do que aprender a fundo algo como o ecossistema Python
Na prática, os símbolos de APL somem no pano de fundo e começam a parecer sintagmas significativos, como quando lemos palavras e frases em inglês, não letra por letra
Na maioria das linguagens isso é impossível, mas se a linguagem for suficientemente concisa e expressiva, volta a ser possível em uma escala considerável
Sempre me vem à cabeça a ideia de que Arthur Whitney realmente odeia rolagem
Também não é preciso ficar com 20 arquivos abertos seguindo “ir para definição”
Quando o programa inteiro cabe em uma página, isso desaparece, e você passa a navegar com o movimento dos olhos
Sinto sinceramente que preciso dedicar tempo para aprendê-las
Isso também se conecta profundamente às dificuldades que tenho enfrentado nas últimas semanas
Estou olhando para um código Python legado acoplado demais, e todas as tentativas anteriores de “melhoria” consistiram em empilhar mais abstrações sobre um modelo de dados errado
Lendo o código linearmente, não dá para saber qual método altera o objeto de entrada
Alguns alteram, outros não, e às vezes retornam o mesmo argumento de entrada sem alteração nenhuma
Eu preferiria uma string mágica que eu pudesse analisar e entender a um mar de desvios em que uma factory devolve vários calculadores que nem sequer compartilham a mesma interface
Porque todas as operações são O(n)
Dá para usar operadores genéricos, mas é preciso entender cuidadosamente o que os pares de valores significam na lógica do domínio e como manter a estrutura correta em cada operação
Quem lê o programa pela primeira vez terá a mesma dificuldade para entender o significado do domínio de negócio, não as operações primitivas
Se há uma melhoria, acho que não é por colocar a complexidade em outro lugar, mas porque o código e os valores reais ficam visíveis ao mesmo tempo
O que torna a programação complexa mais fácil é ver lado a lado os dados em tempo de execução e as operações do código, e é por isso que as ferramentas de IDE continuam melhorando depuradores e inspetores para mostrar o que o programa faz em cada etapa
Nesse contexto, seja abstraindo parte das operações ou parte da estrutura de dados, criar uma nova abstração boa e concisa continua sendo algo bom
O código de inserção do exemplo é, em grande parte, não a adição desejada, mas uma preparação de dados para transformar a forma que o interpretador permite digitar na forma necessária
⍪←é a adição de fato, e↓⍉↑()()()está mais para parsing e conversão de entrada para contornar limitações do APL e do parser de entrada do interpretadorO código de exclusão também precisa criar um array booleano sem relação com o domínio do problema, por exemplo envolvendo
'buggy'para encontrá-lo como um único elemento de um array aninhadoDizer que “cria um hashmap de valores vetoriais” também induz a erro, já que não há hashing de verdade
Não há verificação de chaves duplicadas, não é possível escolher o hash nem ajustar velocidade e distribuição, e as chaves são apenas anexadas em ordem, tornando a busca lenta
O Dyalog APL também tem o I-Beam 1500, um comando mágico do interpretador que marca arrays como alvos de hashing interno para consultas rápidas, mas é preciso continuar lembrando que a abstração interna vaza
Boas ideias de design de linguagens e ferramentas, como “poço do sucesso”, “deve haver apenas uma maneira”, “a primeira maneira que vem à cabeça deve ser a correta” e “tarefas diferentes devem parecer diferentes”, faltam em APL
Em Python ou C#, uma sintaxe como
kv={'a':1, 'b':2}simplesmente funciona; se você esquecer uma chave ou dois-pontos, parece claramente errado, e o editor e o compilador ajudamImplementações de APL dependem de funções mágicas do interpretador como
⎕NGET,⎕CSVe⎕JSONpara entrada e saída, e tratamento de erros, logging e depuração também são fracosA expressão inteira é executada como uma unidade só, e hooks e forks dificultam dividi-la facilmente
No fim, só é possível experimentar e aprender quando se desenvolve uma intuição precisa para várias formas de arrays, por que
⊂3parece não fazer nada, a diferença entre⊆e⊂, expansão escalar etc.Mesmo um padrão imediatamente acessível como “se a chave existir, atualize; se não, adicione” exige em APL repensar desde a forma de ramificação
Em Python, isso se resolve com
if/elseekey in map; em C#, comif/elseemap.Contains(key), mas em APL você acaba caindo na preocupação de reimplementar funcionalidades básicasÉ parecido com o argumento de Aaron Hsu, mas dá a sensação de Up-Goer 5 ou Toki Pona, em que não se pode dizer “caminhão de bombeiros” e é preciso dizer “o carro da pessoa que faz o trabalho de parar o fogo”
[1] https://docs.dyalog.com/latest/CheatSheet%20-%20I-Beams.pdf
[3] https://aplwiki.com/wiki/Scalar_extension
[4] https://xkcd.com/1133/