Une fonction obtenant une valeur d'une autre fonction est-elle considérée comme pure?

9

J'essaie de trouver un moyen de gérer les valeurs de variable par défaut lors de la création de fonctions sans effets secondaires et j'ai fini par ce qui suit:

function getDefaultSeparator() {
    return ':';
}

function process(input, separator) {
    var separator = separator || getDefaultSeparator();

    // Use separator in some logic

    return output;
}

Le séparateur par défaut sera utilisé dans d'autres fonctions et je ne veux le définir qu'en un seul endroit.

S'il s'agit d'une fonction pure, quelle est la différence entre l'utilisation d'une constante DEFAULT_SEPARATOR globale à la place?

agost
la source
5
Il n'y a pas de différence significative, sauf si vous prévoyez d'utiliser la fonction comme espace réservé pour une logique à ajouter plus tard.
Robert Harvey
3
Copie possible de Est-ce qu'une fonction est immédiatement impure si elle prend une fonction comme paramètre? . La question n'est pas un double exact, mais la réponse doit être la même. ("Cela dépend de la pureté de l'autre fonction.")
jpmc26
1
L'utilisation d'une constante globale ne rend pas une fonction impure. L'utilisation d'une valeur globale que vous supposez constante est le cas.
chepner
Btw, vous pouvez curry process(avec l'ordre des paramètres inversé) et spécialiser la fonction curry pourvar processDefault = process(":")
bob

Réponses:

22

Une fonction obtenant une valeur d'une autre fonction est-elle considérée comme pure?

Cela dépend de ce que fait l'autre fonction et de ce que fait la fonction appelante. L'impureté est contagieuse, la pureté ne l'est pas.

L'appel d'une fonction pure ne change pas la pureté de la fonction appelante. L'appel d'une fonction impure rend automatiquement la fonction d'appel impure également.

Donc, dans votre exemple, cela dépend de la pureté de la partie que vous avez omise: si c'est pur, alors toute la fonction est pure.

S'il s'agit d'une fonction pure, quelle est la différence entre l'utilisation d'une constante DEFAULT_SEPARATOR globale à la place?

Rien. Une fonction qui renvoie toujours la même valeur est impossible à distinguer d'une constante. En fait, c'est exactement ainsi que les constantes sont modélisées dans le λ-calcul.

Jörg W Mittag
la source
2
"L'appel d'une fonction impure rend automatiquement la fonction d'appel impure également" En êtes-vous bien sûr? AFAICS, l'appel d'une fonction impure ne rend pas automatiquement l'appelant impur, bien que ce soit le cas.
Déduplicateur
2
@Deduplicator: dépend de la quantité d'analyse statique que vous pouvez (être gêné) de faire. Bien sûr, s'il y a une fonction funcqui a des effets secondaires lorsque vous passez 0, mais pas lorsque vous passez 1, alors vous pouvez raisonnablement dire que funcmême si elle est "impure", une fonction l'appelant comme func(1)(et en ignorant la valeur de retour, disons dire) n'est pas nécessairement impur. Appeler funcest suffisant pour «corrompre» l'appelant comme étant potentiellement impur, mais une fonction contaminée pourrait, par certains moyens, se révéler être pure après tout. Au moins en javascript, où pure / impure n'est pas défini dans le langage.
Steve Jessop
6

Oui, ce sont deux fonctions pures (en supposant que la partie élidée est également pure) parce que:

  1. Le résultat dépend uniquement des paramètres.
  2. Il n'y a aucun effet secondaire.

Notez que si ce getDefaultSeparator()n'était pas une fonction pure, alors aucun ne process()serait pur.

En Javascript, il n'y a pas de différence significative entre l'utilisation d'une fonction pure ou d'une constante et les deux peuvent être utilisées par la fonction pure, tant que la capacité de Javascript à redéfinir les fonctions ou à modifier les valeurs des constantes est évitée.

Un concept clé derrière les fonctions pures est qu'elles pourraient être remplacées par la valeur qu'elles renvoient sans affecter les résultats du programme.

8bittree
la source
1

Comme le disent les autres, bien sûr, c'est toujours une fonction pure.

Cependant, parlons des problèmes de conception. Vous avez raison d'essayer de faire quelque chose pour garder le code SEC, en mettant la valeur en une seule fois. De plus, ce qui, à mon avis, devrait également être pris en considération est le niveau de couplage approprié.

L'utilisation d'une fonction vous donne plus de flexibilité pour changer l'implémentation, c'est-à-dire que l'approche de la fonction offre un couplage plus lâche qu'une variable globale.

La question est de savoir si l'on en a besoin ou non?

Si les consommateurs et le fournisseur sont dans le même module et que le fournisseur est privé du module, il est difficile de prétendre que ce niveau de couplage lâche est nécessaire, car si le fournisseur nécessite une mise à niveau d'une variable privée vers un méthode privée, une refactorisation simple au sein du module peut être appliquée aux consommateurs en même temps. Utiliser une méthode / fonction avant d'en avoir vraiment besoin pourrait tomber sous YAGNI.

Même si le (s) consommateur (s) et le fournisseur sont dans des modules différents, et pourtant les modules sont versionnés ensemble (par exemple, vous utilisez un minifieur, afin que les modules des consommateurs et du fournisseur soient dans le même fichier), YAGNI peut également s'appliquer.

D'un autre côté, si, par exemple, le producteur se trouve dans une bibliothèque ou un package ou module d'API qui est versionné séparément du ou des consommateurs, alors l'utilisation de la fonction peut être appropriée. Dans ce cas, nous devrions examiner la longévité de l'API et des principes comme l'OCP.

(Sur une autre note, si votre code est d'une taille significative, j'encouragerais l'utilisation de modules avec des champs et des méthodes plutôt que des variables et des fonctions globales.)

Erik Eidt
la source