Calcular se uma função é pura

11

Conforme Wikipedia:

Na programação de computadores, uma função pode ser descrita como pura se ambas as afirmações sobre a função se mantiverem: A função sempre avalia o mesmo valor de resultado, dados os mesmos valores de argumento. O valor do resultado da função não pode depender de nenhuma informação ou estado oculto que possa mudar à medida que a execução do programa prossegue ou entre diferentes execuções do programa, nem pode depender de nenhuma entrada externa de dispositivos de E / S. A avaliação do resultado não causa nenhum efeito colateral ou saída semanticamente observável, como mutação de objetos mutáveis ​​ou saída para dispositivos de E / S.

Gostaria de saber se é possível escrever uma função que calcule se uma função é pura ou não. Exemplo de código em Javascript:

function sum(a,b) {
    return a+b;
}

function say(x){
    console.log(x);
}

isPure(sum) // True
isPure(say) // False
Oni
fonte
7
Isso pode ser candidato ao Computer Science Stack Exchange . Isso tende mais ao domínio da teoria ("possível") do que à prática e ao design ... Está um pouco além do meu conhecimento.
... e mesmo que seja "possível" e "teoria", provavelmente existe uma resposta real (uma boa opção para perguntas e respostas) que pode até envolver uma prova ... o que novamente o leva ao mundo da ciência da computação .
Eu acho que você pode determinar se uma função é pura olhando para o tipo de funções que são invocadas em sua definição. Quero dizer, você pode definir pureza por indução na estrutura das funções.
Giorgio
7
Não é possível prescindir dos hacks de reflexão específicos da plataforma. Se uma função é uma caixa preta, nenhum experimento pode provar que é pura. Por exemplo, se houver algo parecido if (rand(1000000)<2) return WRONG_ANSWER, investigar a função várias vezes para obter um comportamento consistente não ajudará. Mas, se você tiver acesso à definição da função, a prova é trivial.
SK-logic
1
@ user470365 A partir da definição de uma função pura - "A avaliação do resultado não causa nenhum efeito colateral ou saída semanticamente observável, como mutação de objetos mutáveis ​​ou saída para dispositivos de E / S". - Tudo o que escreve no IO é impuro por definição. No exemplo, sayas chamadas console.logque é impuro, assim, sayé impuro também.

Respostas:

17

Sim, é possível, dependendo do idioma.

Em JavaScript, você pode saber se uma função é pura pelos seguintes critérios:

  • Ele lê apenas parâmetros e locais;

  • Ele apenas escreve locais;

  • Em pessoas não locais, chama apenas funções puras;

  • Todas as funções que chama implicitamente são puras, por exemplo toString; e

  • Ele grava propriedades de habitantes locais apenas se eles não forem alias de habitantes locais.

O alias não é possível determinar em JavaScript no caso geral, porque você sempre pode procurar propriedades de um objeto dinamicamente ( object["property"]). Desde que você nunca faça isso e tenha a fonte de todo o programa, acho que o problema é tratável. Você também precisaria de informações sobre quais funções nativas têm efeitos colaterais, como console.logquase tudo o que envolve o DOM.

O termo "puro" também pode usar alguns esclarecimentos. Mesmo em uma linguagem de programação puramente funcional, tipicamente estática, onde todas as funções são referencialmente transparentes, uma função ainda pode falhar ao finalizar. Então, quando falamos sobre id :: a -> a, o que realmente estamos dizendo não é:

Dado algum valor do tipo a, a função idproduz um valor do tipo a.

Mas sim:

Dado algum valor do tipo a, a função idque não produzem um valor que é não do tipo a.

Porque uma implementação válida de idé error "Not implemented!". Como Peteris aponta, essa não totalidade pode ser vista como uma espécie de impureza. Koka é uma linguagem de programação funcional - com sintaxe modelada em JavaScript - que pode inferir possíveis efeitos como divergência (não terminação), transparência referencial, lançamento de exceções e ações de E / S.

Jon Purdy
fonte
+1 - Dada a resposta por Peteris tem tantos upvotes .....
mattnz
Suponho que você precisaria adicionar "Ele chama apenas funções puras" à lista de critérios.
Greg Hewgill
1
@GregHewgill: Boa captura. Eu atualizei a resposta de acordo. Não há problema em chamar funções de mutação nos locais, desde que não tenham efeito colateral. “Pure” é muito sobrecarregado um termo ...
Jon Purdy
Você também precisa verificar se toStringé puro para qualquer objeto usado como string.
amigos estão dizendo sobre oleg V.
Não acho que essa resposta esteja correta, porque há apenas um subconjunto de circunstâncias em que você pode realmente fazer essas determinações. Considere: essa função é pura? function a (o) { return o.method(); }- na verdade, não podemos responder a isso, porque depende de qual parâmetro oé passado. Além disso, não podemos explicar o que acontece se uma função pura anteriormente certificada for alterada para uma implementação não pura, o que é sempre um problema em potencial com o javascript.
Jules
11

Não. Você pode verificar facilmente se uma função executa apenas operações "de segurança pura", conforme descrito na resposta de Jon Purdy, mas isso não é suficiente para responder à pergunta da IMO.

Considere esta função:

function possiblyPure(x) {
    if (someCheck(x)) {
        return x+1; // pure code path
    }
    else {
        console.log("I'm so unpure..."); // unpure code path
    }
}

Obviamente, se não someChecké puro, também é possiblyPure. Mas, se someChecké puro e retorna truepara todo valor possível de x, possiblyPureé puro, pois o caminho do código não puro é inacessível!

E aqui vem a parte difícil: determinar se someCheckretorna ou não verdadeiro para cada entrada possível. Tentar responder a essa pergunta imediatamente leva você ao reino do problema de parada e de problemas indecidíveis semelhantes.

EDIT: Prova de que é impossível

Existe alguma incerteza se uma função pura deve terminar em todas as entradas possíveis. Mas em ambos os casos, o problema de parada pode ser usado para mostrar que a verificação de pureza é impossível.

Caso A) Se uma função pura for necessária para terminar em todas as entradas possíveis, você precisará resolver o problema de parada para determinar se a função é pura ou não. Como isso é conhecido por ser impossível, por essa definição, a pureza não pode ser calculada.

Caso B) Se uma função pura tiver permissão para não terminar em algumas entradas, podemos construir algo assim: Vamos supor que isPure(f)calcule se fé uma string que define uma função pura.

function halts(f) {
   var fescaped = f.replace(/\"/g, '\\"');
   var upf = 'function() { '+f+'("'+fescaped+'\); console.log("unpure"); }';
   return isPure(upf);
}

Agora, isPureé necessário determinar se a fparada deve ou não ser sua própria fonte como entrada. Se upfparar , não é puro; se não terminar, upfé puro se fé puro.

Se isPurefuncionasse como esperado (retorna resultados corretos e termina em cada entrada), teríamos resolvido o problema de parada (*)! Uma vez que se sabe que isso é impossível, isPurenão pode existir.

(*) para funções JavaScript puras, o que é suficiente para resolvê-lo também para a máquina de turing.

user281377
fonte
3
Verdade. Sempre é possível fazer uma análise conservadora - para verificar se uma função é definitivamente pura, mas não é possível verificar se ela definitivamente não é pura.
SK-logic
Muitos casos são trivialmente decidíveis - aquelas funções puras descritas por Jon Purdy ou funções impuras que, incondicionalmente, fazem algo sujo; mas, em geral, é possível construir casos indecidíveis.
user281377
1

Esta questão de stackoverflow tem uma resposta de yfeldblum que é relevante aqui. (E tem um voto negativo por algum motivo que não consigo entender. Seria má etiqueta para votar em algo que tem 3 anos?) Ele dá uma prova de que se uma função é pura é redutível ao problema de interrupção em um comentário.

Eu acho que, do ponto de vista prático, não seria muito difícil para alguns idiomas se você deixar a função retornar sim, não ou talvez. Eu estava assistindo a um vídeo sobre Clojure alguns dias atrás, e o palestrante havia feito uma contagem de instâncias de impureza em uma base de código, procurando por cerca de 4 strings diferentes (como "ref"). Devido à ênfase de Clojure na pureza e segregação de coisas impuras, foi trivial, mas não era exatamente o que você estava procurando.

Então, teoricamente impossível, praticamente possível se você ajustar um pouco a questão, e acho que o quão difícil seria dependeria muito da linguagem. Linguagens mais simples / limpas, com foco na imutabilidade e na boa reflexão, seriam mais fáceis.

Michael Shaw
fonte
Eu acho que isso foi prejudicado porque está errado. bottomé um valor válido de primeira classe, não merece ser discriminado dessa maneira.
SK-logic
0

Ótima pergunta.

O melhor que você pode fazer na prática, assumindo que não há capacidade de ouvir ações de E / S para chamar a função quantas vezes for possível. Então veja se o valor de retorno é consistente.

Mas você não pode fazer isso em geral. Pode-se argumentar que os programas sem interrupção não são puros e não podemos decidir o problema da interrupção.

Peter é
fonte
1
-1: Seria além do trivial escrever uma função que passe neste teste e é tudo menos pura.
mattnz
3
Por essa lógica, qualquer voidfunção seria "pura", o que é claramente falso.
Greg Hewgill
1
@ Greg: Por extensão, void foo (void) também teria que ser puro.
mattnz
0

Não é possível no caso geral. Veja o problema de parada . Resumidamente, é impossível escrever um programa que, dada uma função e entrada arbitrárias, determine se o programa será interrompido ou executado para sempre. Se funcionar para sempre, não é uma função pura que se ajusta à definição que você deu.

dbkk
fonte
5
Correr para sempre não parece descontar uma função do ajuste de seus critérios para uma função pura.
Whatsisname
+1: Há um implícito necessário que os termina functiuon por "A função sempre avalia o mesmo valor do resultado give ...."
mattnz
2
Consistentemente funcionando para sempre sem modificar nenhum estado é perfeitamente "puro". Mas, é claro, é uma questão terminológica aqui.
SK-logic
@mattnz, essa função sempre avaliará o bottomvalor.
SK-logic
1
Eu posso ver onde entra a questão da terminologia. Em algumas interpretações, uma função "pura" é determinística e nunca comunica nenhum estado ou valor com o exterior durante sua execução. Em outras interpretações, a interrupção é adicionada aos requisitos. Com a primeira interpretação, é fácil determinar se uma função é pura: uma máquina que executa uma linguagem específica deve ser capaz de determinar se um programa nessa linguagem faz alguma comunicação com o exterior.
rwong