Faut-il *ghoster* les noms de variable ?

En programmation fonctionnelle, il m’arrive souvent d’écrire ce type de code :

let x = f(...) in
let x = g(...) in
let x = h(...) in
...

Les deux premières variables deviennent alors inutilisables car elles sont cachées par la dernière déclaration. Mais en général, ce n’est pas un soucis car on en a pas besoin par la suite, ce sont juste des valeurs temporaires. Mais est-ce une bonne pratique ?

Premièrement, pour des raisons d’efficacité, on ne peut pas vraiment se passer des variables temporaires, et même si on le pouvait, en général, cela donne un code à rallonge très difficile à lire.

Deuxièmement, imaginons que j’ai donné des noms différents :

let x = f(...) in
let x' = g(...) in
let x'' = h(...) in
...

Alors le corps de la fonction peut parler de x, x' ou x''. Or, au début, ce qui m’intéressait, c’était seulement x''. Cela peut donc être source d’erreur dans mon code qui parfois s’avère compliqué à repérer.

Je m’autorise donc de changer les noms seulement si les types de x, x' et x'' sont différents. Ainsi, si je me suis trompé, je me prendrai un avertissement de la part du type-checker. Si les types sont les mêmes, alors il me semble que c’est une bonne pratique de ghoster les noms de variables pour éviter ce type de problème.

Un autre cas où il m’arrive de masquer le nom d’une variable est lorsqu’une fonction a besoin d’une fonction auxliaire :

let pp_list pp fmt list =
  let rec pp_list pp fmt = function
  | [] -> Format.fprintf fmt "]"
  | [x] -> Format.fprintf fmt "%a]" pp x
  | x::t ->  Format.fprintf fmt "%a;%a" pp x (pp_list pp) t
  in
  Format.fprintf fmt "[%a" (pp_list pp)  list


3 commentaires

C’est rigolo, je fais très exactement l’inverse en Rust. Si les types sont différents, je m’autorise à masquer les déclarations précédentes parce que si je me met à vouloir utiliser un résultat intermédiaire au lieu du résultat final, j’aurai une erreur de compilation sur les types.

Après, ça me parait en particulier une bonne pratique lorsque tu veux correctement agencer un bout de code qui devient trop gros ou le séparer logiquement, mais que les états intermédiaires sont d’autres représentations de la même chose. Typiquement si tu as une chaine de caractères contenant des indices séparés par des espaces, tu peux appeler ça indices et séparer la chaine, puis convertir en entier, puis faire ce que tu veux avec en plusieurs fois, en appelant ça indices à chaque étapes.

Par contre, je remplace ça par du chaining d’appel de fonction dès que je le peux (map, map_err, etc).

C’est rigolo, je fais très exactement l’inverse en Rust. Si les types sont différents, je m’autorise à masquer les déclarations précédentes parce que si je me met à vouloir utiliser un résultat intermédiaire au lieu du résultat final, j’aurai une erreur de compilation sur les types.

unidan

Ben, tu ne peux pas utiliser un résultat intermédiaire si tu masques une variable justement.

+0 -0

Moi je dis "shadower" plutôt que "ghoster", par référence au phénomène qui s’appelle le "shadowing" en anglais. "variable ghost" a un autre sense dans les langages faits pour la vérification, il vaut mieux ne pas mélanger.

Quand le fait d’utiliser une vieille variable serait une erreur, il faut soit shadower, soit utiliser des noms suffisamment différents pour qu’on ne puisse pas se tromper facilement.

Par exemple, si on veut garantir qu’une entrée est normalisée/sanitisée avant d’être utilisée:

let f x =
  let x = normalize x in
  ...

ou bien

let f x_non_normal =
  let x = normalize x_non_normal in
  ...

sont tous les deux raisonnables en pratique.


Certains programmeurs font en effet la chasse au shadowing, en essayant de l’interdire. Personellement je trouve cette attitude très suspecte. Je ne l’ai vu mettre en pratique que dans deux contextes, en Erlang, dont la syntaxe est assez moisie (elle reprend la syntaxe Prolog, sans la sémantique pratique qui allait avec), et où les x1=...; x2=...; x3=... sont souvent agaçants et sources d’erreurs, et en CoffeeScript, dont les créateurs avaient décidé d’interdire le shadowing en supprimant la construction de déclaration de nouvelle variable, ce qui est une belle connerie.

+5 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte