Disons que nous avons une fonction pure normale telle que
function add(a, b) {
return a + b
}
Et puis nous le modifions pour qu'il ait un effet secondaire
function add(a, b) {
writeToDatabase(Math.random())
return a + b;
}
Autant que je sache, ce n’est pas considéré comme une fonction pure, car j’entends souvent les gens appeler des fonctions pures "des fonctions sans effets secondaires". Cependant, il se comporte comme une fonction pure dans la mesure où il renvoie la même sortie pour les mêmes entrées.
Existe-t-il un nom différent pour ce type de fonction, est-il anonyme, ou est-il toujours pur et je me trompe au sujet de la définition de la pureté?
writeToDatabase
échec, il pourrait déclencher une exception et faire en sorte que votre deuxièmeadd
fonction génère parfois une exception, même si elle est appelée avec les mêmes arguments qu'avant, cela ne posait pas de problème auparavant. "pureté entrée-sortie".F(x)
est défini pour retournertrue
s'il est appelé avec le même argument que l'appel précédent. Clairement, avec la séquence,{1,2,2} => {undefined, false, true}
c'est déterministe, mais cela donne différents résultatsF(2)
.Réponses:
Je ne suis pas sûr des définitions universelles de la pureté, mais du point de vue de Haskell (un langage dans lequel les programmeurs ont tendance à se préoccuper de choses telles que la pureté et la transparence référentielle), seule la première de vos fonctions est "pure". La deuxième version de
add
n'est pas pure . Donc, en réponse à votre question, je l'appellerais "impur";)Selon cette définition, une fonction pure est une fonction qui:
Avec cette définition, il est clair que votre deuxième fonction ne peut pas être considérée comme pure, car elle enfreint la règle 2. Autrement dit, les deux programmes suivants ne sont PAS équivalents:
et
En effet, même si les deux fonctions renverront la même valeur, la fonction
f
écrira deux fois dans la base de données, maisg
une fois! Il est très probable que les écritures dans la base de données fassent partie du comportement observable de votre programme. Dans ce cas, j'ai montré que votre deuxième version deadd
n'est pas "pure".Si les écritures dans la base de données ne font pas partie du comportement de votre programme, les deux versions de
add
peuvent être considérées comme équivalentes et pures. Mais je ne peux pas penser à un scénario où écrire dans la base de données importe peu. Même l'exploitation forestière compte!la source
f(x)
dépend non seulement dex
, mais aussi de certaines variables globales externesy
. Ensuite, sif
a la propriété de RT, vous pouvez échanger librement toutes ses occurrences avec sa valeur de retour tant que vous ne touchez pasy
. Oui, mon exemple est douteux. Mais l’important est le suivant: si l’f
écriture dans la base de données (ou l’écriture dans un journal) perd la propriété de RT: peu importe que vous laissiez globalementy
intact, vous savez que la signification de votre programme change en fonction de votre situation réelle. appelezf
ou utilisez simplement sa valeur de retour.Une telle fonction s'appelle
En ce qui concerne l'état:
Selon la définition de la fonction que vous utilisez, une fonction n'a pas d'état. Si vous venez du monde orienté objet, rappelez-vous que
x.f(y)
c'est une méthode. En fonction, cela ressembleraitf(x,y)
. Et si vous êtes dans des fermetures avec une portée lexicale incluse, rappelez-vous que cet état immuable pourrait aussi bien faire partie de l'expression des fonctions. Ce n'est que l'état mutable qui aurait un impact sur la nature déterministe des fonctions. Donc, f (x) = x + 1 est déterministe tant que le 1 ne change pas. Peu importe où le 1 est stocké.Vos fonctions sont déterministes. Votre première est également une fonction pure. Votre seconde n'est pas pure.
Le point 1 signifie déterministe . Le point 2 signifie transparence référentielle . Ensemble, ils signifient qu'une fonction pure ne permet que ses arguments et sa valeur renvoyée à changer. Rien d'autre ne provoque le changement. Rien d'autre n'est changé.
la source
Math.random()
. Donc non, sauf si nous supposons un PRNG (au lieu d'un RNG physique) ET considérons que les PRNG font partie de l'entrée (ce qui n'est pas le cas, la référence est codée en dur), ce n'est pas déterministe.Si vous ne vous souciez pas de l'effet secondaire, il est transparent par référentiel. Bien sûr, il est possible que vous ne vous en souciiez pas, mais que quelqu'un d'autre le fasse. L'applicabilité du terme dépend donc du contexte.
Je ne connais pas de terme général pour désigner précisément les propriétés que vous décrivez, mais un sous-ensemble important est celui qui est idempotent . En informatique, un peu différemment en mathématiques *, une fonction idempotente est une fonction qui peut être répétée avec le même effet; c’est-à-dire que l’effet net de le faire plusieurs fois est le même que de le faire une fois.
Donc, si votre effet secondaire était de mettre à jour une base de données avec une certaine valeur dans une certaine ligne, ou de créer un fichier avec un contenu parfaitement cohérent, alors ce serait idempotent , mais s'il était ajouté à la base de données ou ajouté à un fichier alors ce ne serait pas.
Les combinaisons de fonctions idempotentes peuvent être ou non idempotentes dans leur ensemble.
* En informatique, l'utilisation d' idempotent différemment des mathématiques semble provenir d'une utilisation incorrecte du terme mathématique qui a ensuite été adopté parce que le concept est utile.
la source
(f x, f x)
aveclet y = f x in (y, y)
se déroulera en dehors de l' espace disque-exceptions deux fois plus vite que vous pourriez dire que ce sont vous ne vous souciez pas des cas extrêmes, mais avec une définition aussi floue, nous pourrions aussi bien appeler unnew Random().Next()
référentiel transparent parce que bon sang, je me moque du nombre que je reçois de toute façon.Random.Next
in .NET a effectivement des effets secondaires. Tout à fait. Si vous le pouvezNext
, attribuez-la à une variable, puis rappelez-laNext
et affectez-la à une autre variable. Il est probable qu'elles ne seront pas égales. Pourquoi? Parce que l'invocationNext
change quelque état interne caché dans l'Random
objet. C'est l'opposé de la transparence référentielle. Je ne comprends pas votre affirmation selon laquelle les "effets principaux" ne peuvent être des effets secondaires. Dans le code impératif, il est plus courant que l'effet principal soit un effet secondaire, car les programmes impératifs sont par nature dynamiques.Je ne sais pas comment on appelle de telles fonctions (ni même s'il y a même un nom systématique), mais j'appellerais une fonction qui n'est pas pure (comme les autres réponses sont étouffées) mais qui retourne toujours le même résultat si elle est fournie avec les mêmes paramètres " paramètres "(comparé à la fonction de ses paramètres et à un autre état). J'appellerais cela juste une fonction, mais malheureusement, lorsque nous parlons de "fonction" dans le contexte de la programmation, nous entendons quelque chose qui ne doit pas nécessairement être une fonction réelle.
la source
Cela dépend fondamentalement de si vous vous souciez de l'impureté ou non. Si la sémantique de cette table est que vous ne vous souciez pas du nombre d'entrées, alors c'est pur. Sinon, ce n'est pas pur.
Autrement dit, tant que les optimisations basées sur la pureté n'enfreignent pas la sémantique du programme, c'est bien.
Un exemple plus réaliste serait si vous essayiez de déboguer cette fonction et d’ajouter des instructions de journalisation. Techniquement, la journalisation est un effet secondaire. Les bûches le rendent-il impur? Non.
la source
Je dirais que la meilleure chose à demander n'est pas comment nous l'appellerions, mais comment nous analyserions un tel code. Et ma première question clé dans une telle analyse serait:
Ceci est simple à illustrer dans Haskell (et cette phrase n’est qu’une demi-plaisanterie). Un exemple du cas "non" serait quelque chose comme ceci:
Dans cet exemple, l'action que nous effectuons (imprimer la ligne
"I'm doubling some number"
) n'a aucun impact sur la relation entrex
et le résultat. Cela signifie que nous pouvons le refactoriser de cette manière (en utilisant laApplicative
classe et son*>
opérateur), ce qui montre que la fonction et l'effet sont en fait orthogonaux:Donc, dans ce cas, je dirais personnellement que c'est un cas où vous pouvez factoriser une fonction pure. Cela concerne beaucoup de programmes Haskell - apprendre à décomposer les parties pures du code efficace.
Un exemple du type "oui", où les parties pures et effectives ne sont pas orthogonales:
Maintenant, la chaîne que vous imprimez dépend de la valeur de
x
. La partie fonction (multiplierx
par deux), cependant, ne dépend pas du tout de l'effet, nous pouvons donc la factoriser:Je pourrais continuer à donner d’autres exemples, mais j’espère que c’est suffisant pour illustrer le point que j’avais commencé: vous n’appelez pas quelque chose, vous analysez la relation entre les parties pure et efficace et les factorisez quand c’est le cas. à votre avantage.
C'est l'une des raisons pour lesquelles Haskell utilise
Monad
tellement sa classe. Les monades sont (entre autres) un outil pour effectuer ce type d’analyse et de refactorisation.la source
Les fonctions destinées à provoquer des effets secondaires sont souvent qualifiées d' effet . Exemple https://slpopejoy.github.io/posts/Effectful01.html
la source