Portanto, digamos, a consulta ao banco de dados ou ao arquivo de gravação não poderia ser feita em um estilo funcional puro por definição. Por exemplo, é uma das razões pelas quais precisamos de mônadas.
Ninguém "precisa" de mônadas, é apenas uma maneira de descrever as coisas. Na verdade, provavelmente nem é o melhor caminho. Alguma forma de digitação de efeito , tipos de exclusividade ou um sistema baseado em lógica linear completa parece mais persuasivo na teoria, mas são todos desvios mais radicais de sistemas de tipos conhecidos e mais difíceis de expressar. O IO monádico, como encontrado em Haskell, é um compromisso entre usabilidade e simplicidade, pois modela essencialmente a programação totalmente imperativa de uma maneira que coexistiu facilmente com o sistema de tipo ML existente, já usado na linguagem.
A questão é: por que consideramos a saída STDOUT como algo impuro? Sim, qualquer manipulador de arquivos é arriscado - nunca podemos ter certeza de que os dados sempre serão gravados. Mas e o STDOUT? Por que devemos pensar nisso como algo não confiável? É mais confiável que a própria avaliação? Quero dizer, sempre podemos puxar o gatilho e, assim, interromper o cálculo.
Não é, e nós não. A entrada e saída do programa como um todo pode ser simplesmente considerada como argumento e resultado do tratamento de todo o programa como uma grande função pura. Contanto que ele imprima a mesma coisa no stdout, se você alimentá-lo da mesma maneira do stdin, ainda é uma função pura. De fato, antes de introduzir E / S monádica, a Haskell usava um sistema de E / S baseado em fluxo que usava fluxos preguiçosos puros para entrada e saída. Ele foi descartado porque aparentemente era uma dor de se usar, o que pode lhe dar uma idéia do porquê você nunca ouviu falar de algo assim. :]
Para esclarecer a questão de maneira mais tola, considere a linguagem esotérica minimalista, Lazy K :
Lazy K é uma linguagem de programação funcional referencialmente transparente e coletada pelo lixo, com um sistema de E / S simples baseado em fluxo.
O que distingue o Lazy K de outros idiomas é a quase total falta de outros recursos. Por exemplo, ele não oferece um sistema integrado tipo polimórfico Hindley-Milner. Ele não é fornecido com uma extensa biblioteca padrão com suporte para programação de GUI independente de plataforma e ligações para outros idiomas. Nem uma biblioteca desse tipo pode ser escrita, pois, entre outras coisas, o Lazy K não fornece nenhuma maneira de definir ou referir-se a outras funções além de embutidas. Essa incapacidade é complementada por uma falta de suporte correspondente a números, seqüências de caracteres ou qualquer outro tipo de dados. No entanto, Lazy K é Turing-completo.
(...)
Os programas K preguiçosos vivem no mesmo reino platônico atemporal que as funções matemáticas, o que a página de Unlambda chama de "o reino abençoado do cálculo lambda puro e sem tipo". Assim como a coleta de lixo oculta o processo de gerenciamento de memória do programador, a transparência referencial oculta o processo de avaliação. O fato de que algum cálculo é necessário para exibir uma imagem do conjunto de Mandelbrot ou para "executar" um programa Lazy K é um detalhe da implementação. Essa é a essência da programação funcional.
(...)
Como lidar com entrada e saída em um idioma sem efeitos colaterais? Em certo sentido, entrada e saída não são efeitos colaterais; eles são, por assim dizer, efeitos de frente e de trás. O mesmo ocorre no Lazy K, onde um programa é simplesmente tratado como uma função, do espaço de entradas possíveis ao espaço de saídas possíveis.
Duvido que você encontre uma linguagem mais puramente funcional que essa!
Lembre-se, no entanto, de que o acima se aplica somente a essencialmente pegar a entrada e saída de uma função pura e conectá-las ao stdin / stdout "externamente" de alguma maneira. Há uma grande diferença entre isso e ter acesso às primitivas de E / S reais no nível do sistema. Os detalhes de implementação de leitura e gravação nos fluxos podem vazar impureza, a menos que sejam encapsulados com cuidado.
Espero que esse seja o principal motivo pelo qual você não pode fazer isso diretamente no Haskell - os casos de uso sensatos são escassos em comparação ao uso de E / S monádica e, para o último, há muitos benefícios em ter acesso à coisa real. Acredito que é por isso que, por exemplo, os argumentos da linha de comando para o programa não são simplesmente passados como argumentos main
, mesmo que pareça intuitivamente que deveriam ser.
Você pode recuperar uma versão mínima de algo assim em um programa específico - apenas capture os argumentos como valores puros e use a interact
função para o restante do seu programa.