Nas linguagens FP, chamar uma função com os mesmos parâmetros repetidamente retorna o mesmo resultado repetidamente (ou seja, transparência referencial).
Mas uma função como esta (pseudo-código):
function f(a, b) {
return a + b + currentDateTime.seconds;
}
não retornará o mesmo resultado para os mesmos parâmetros.
Como esses casos são tratados no FP?
Como a transparência referencial é imposta? Ou não é e depende dos programadores se comportarem?
currentDateTime
def
, em idiomas que reforçam a transparência referencial (como Haskell). Vou deixar que outra pessoa fornecer uma resposta mais detalhada :) (dica:currentDateTime
faz IO, e isso vai mostrar em seu tipo)Respostas:
a
eb
sãoNumber
s, enquantocurrentDateTime.seconds
retorna umIO<Number>
. Esses tipos são incompatíveis, você não pode adicioná-los, portanto, sua função não é bem digitada e simplesmente não será compilada. Pelo menos é assim que é feito em linguagens puras com um sistema de tipo estático, como Haskell. Em linguagens impuras como ML, Scala ou F #, cabe ao programador garantir a transparência referencial e, é claro, em linguagens dinamicamente tipadas como Clojure ou Scheme, não existe um sistema de tipo estático para impor a transparência referencial.fonte
Tentarei ilustrar a abordagem de Haskell (não tenho certeza de que minha intuição esteja 100% correta, pois não sou especialista em Haskell, as correções são bem-vindas).
Seu código pode ser escrito em Haskell da seguinte maneira:
Então, onde está a transparência referencial?
f
é uma função que, dados dois inteirosa
eb
, criará uma ação, como você pode ver pelo tipo de retornoIO Integer
. Essa ação sempre será a mesma, considerando os dois números inteiros; portanto, a função que mapeia um par de números inteiros para ações de IO é referencialmente transparente.Quando essa ação é executada, o valor inteiro produzido depende do tempo atual da CPU: executar ações NÃO é uma aplicação de função.
Resumindo: No Haskell, você pode usar funções puras para construir e combinar ações complexas (seqüenciamento, composição de ações etc.) de maneira referencialmente transparente. Novamente, observe que no exemplo acima a função pura
f
não retorna um número inteiro: ela retorna uma ação.EDITAR
Mais alguns detalhes sobre a pergunta JohnDoDo.
O que significa que "executar ações NÃO é uma aplicação funcional"?
Dados os conjuntos T1, T2, Tn, T, uma função f é um mapeamento (relação) que se associa a cada tupla em T1 x T2 x ... x Tn um valor em T. Portanto, a aplicação da função produz um valor de saída, considerando alguns valores de entrada . Usando esse mecanismo, você pode construir expressões que avaliam valores, por exemplo, o valor
10
é o resultado da avaliação da expressão4 + 6
. Observe que, ao mapear valores para valores dessa maneira, você não está executando nenhum tipo de entrada / saída.Em Haskell, ações são valores de tipos especiais que podem ser construídos avaliando expressões contendo funções puras apropriadas que funcionam com ações. Dessa maneira, um programa Haskell é uma ação composta obtida pela avaliação da
main
função. Esta ação principal tem tipoIO ()
.Depois que essa ação composta é definida, outro mecanismo (não o aplicativo de funções) é usado para invocar / executar a ação (veja, por exemplo, aqui ). Toda a execução do programa é o resultado da invocação da ação principal, que por sua vez pode invocar sub-ações. Esse mecanismo de chamada (cujos detalhes internos eu não conheço) cuida da execução de todas as chamadas de E / S necessárias, possivelmente acessando o terminal, o disco, a rede e assim por diante.
Voltando ao exemplo. A função
f
acima não retorna um número inteiro e você não pode escrever uma função que execute E / S e retorne um número inteiro ao mesmo tempo: você deve escolher um dos dois.O que você pode fazer é incorporar a ação retornada por
f 2 3
em uma ação mais complexa. Por exemplo, se você deseja imprimir o número inteiro produzido por essa ação, você pode escrever:A
do
notação indica que a ação retornada pela função principal é obtida por uma composição seqüencial de duas ações menores e ax <-
notação indica que o valor produzido na primeira ação deve ser passado para a segunda ação.Na segunda ação
o nome
x
é vinculado ao número inteiro produzido executando a açãoUm ponto importante é que o número inteiro produzido quando a primeira ação é chamada pode viver apenas dentro das ações de E / S: pode ser passado de uma ação de E / S para a seguinte, mas não pode ser extraído como um valor inteiro simples.
Compare a
main
função acima com esta:Nesse caso, existe apenas uma ação, a saber
putStrLn (show y)
, ey
está vinculada ao resultado da aplicação da função pura+
. Também podemos definir esta ação principal da seguinte maneira:Então, observe a sintaxe diferente
Sumário
fonte
executing actions is NOT function application
frase? No meu exemplo, pretendia retornar um número inteiro. O que acontece se um retorno é um número inteiro?f
não poderá ter o tipoIO Integer
(isso é uma ação, não um número inteiro). Mas, então, ele não pode chamar a "data atual", que tem tipoIO Integer
.Show
disponível. Mas você pode facilmente adicionar um, caso em que o código será compilado e executado muito bem. Não há nada de especial emIO
ações com relação ashow
.A abordagem usual é permitir que o compilador rastreie se uma função é pura por todo o gráfico de chamadas e rejeite o código que declara funções como puras que fazem coisas impuras (onde "chamar uma função impura" também é uma coisa impura).
Haskell faz isso tornando tudo puro na própria linguagem; qualquer coisa impura é executada no tempo de execução, não a própria linguagem. A linguagem apenas constrói ações de E / S usando funções puras. O tempo de execução encontra a função pura chamada
main
doMain
módulo designado , avalia-a e executa a ação (impura) resultante.Outras línguas são mais pragmáticas sobre isso; Uma abordagem comum é adicionar sintaxe para marcar as funções como 'puras' e proibir qualquer ação impura (atualizações de variáveis, chamar funções impuras, construções de E / S) dentro dessas funções.
No seu exemplo,
currentDateTime
é uma função impura (ou algo que se comporta como um), portanto, é proibido chamá-la dentro de um bloco puro e causar um erro no compilador. No Haskell, sua função seria algo como isto:Se você tentou fazer isso em uma função não IO, assim:
... então o compilador diria que seus tipos não fazem check-out -
getCurrentTime
é do tipoIO Time
, nãoTime
, mastimeSeconds
esperaTime
. Em outras palavras, Haskell utiliza seu sistema de tipos para modelar (e reforçar) a pureza.fonte