STDOUT e sua impureza

10

Eu li muitos livros e artigos sobre programação funcional e ainda tenho vergonha de não conseguir entender com certeza alguns conceitos muito básicos.

Uma das principais idéias da programação funcional é que a mesma entrada sempre deve produzir a mesma saída. 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.

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.

shabunc
fonte

Respostas:

6

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 interactfunção para o restante do seu programa.

CA McCann
fonte
Senhor, devo confessar, aprecio sua resposta em qualquer uma das pilhas. Você definitivamente deveria escrever um livro Haskell e eu NÃO estou brincando.
shabunc
@shabunc: Eu às vezes se perguntou o quão perto a soma total de minhas respostas sobre SO devem ser do tamanho de um livro já ...
CA McCann
Você poderia dar um exemplo de um sistema baseado na lógica linear completa? Isso parece interessante, se existir.
configurator
@ configurador: Observe como eu vinculei a idiomas específicos para os outros, mas uma página da Wikipedia para lógica linear? Infelizmente, se eu tivesse um exemplo, eu teria dado. : [Tudo o que eu ouvi foram protótipos parciais e sistemas experimentais da pesquisa em CS. Se você quiser aprofundar isso, aqui estão alguns materiais relativamente acessíveis em sistemas de tipos lineares que podem ajudá-lo a começar.
CA McCann
3

Embora a pureza em um programa funcional seja um objetivo digno, a realidade é que todo programa útil e não trivial terá alguma impureza (ou "efeitos colaterais"), pelos motivos mencionados.

Programas completamente puros são, por definição, uma caixa preta selada e são essencialmente desinteressantes.

A linguagem funcional Haskell resolve esse problema isolando efeitos colaterais como saída em mônadas . A mônada preserva um estilo de programação puramente funcional, enquanto ainda possibilita a produção de saída.

Robert Harvey
fonte
claro, você está certo. Mas entendo por que 100% de pureza é utopia. Eu questiono é apenas sobre STDOUT.
shabunc 15/09/11
11
STDOUT é um efeito colateral, como qualquer outro. Internamente, a mônada executaria qualquer verificação de erro que fosse necessária.
Robert Harvey
sim, é disso que se trata esta questão - por que ela considerou o efeito colateral igual a qualquer outro?
shabunc
2
Qualquer coisa que modifique o mundo exterior é considerado um efeito colateral.
Robert Harvey
1

A gravação no STDOUT pode falhar se você não estiver conectado a um dispositivo de terminal ou se (por algum motivo) fechou o descritor de arquivo.

Além disso, STDOUT nem sempre é "a tela do console". Às vezes, é direcionado para outro programa. Às vezes o tubo está quebrado.

Yam Marcovic
fonte
0

Ajuda se você pensa em pureza nos termos "Muda o estado do mundo exterior". Isso pode incluir a gravação em um console, arquivo de log, ejeção do CD ou "Lançamento dos mísseis".

Também pode ser um problema em termos de execução simultânea. Se você sabe que uma função não tem efeitos colaterais, pode organizar facilmente a concorrência, pois pode provar que não pode haver condições de corrida ou algo semelhante.

Zachary K
fonte
Altera o estado do mundo externo ou depende do estado do mundo externo. Veja esta pergunta para mais discussão nesse sentido.
MatrixFrog