Pensez à la fonction pure ci-dessous, dans un langage impératif :
def foo(x,y):
x = f(x) if a(x)
if c(x):
x = g(x)
else:
x = h(x)
x = f(x)
y = f(y) if a(y)
x = g(x) if b(y)
return [x,y]
Cette fonction représente un style où vous devez mettre à jour les variables de manière incrémentielle. On peut l'éviter dans la plupart des cas, mais il existe des situations où ce modèle est inévitable - par exemple, l'écriture d'une procédure de cuisson pour un robot, qui nécessite par nature une série d'étapes et de décisions. Maintenant, imaginons que nous essayions de représenter foo
en Haskell.
foo x0 y0 =
let x1 = if a x0 then f x0 else x0 in
let x2 = if c x1 then g x1 else h x1 in
let x3 = f x2 in
let y1 = if a y0 then f y0 else y0 in
let x4 = if b y1 then g x3 else x3 in
[x4,y1]
Ce code fonctionne, mais il est trop compliqué et sujettes à des erreurs en raison de la nécessité de gérer manuellement les étiquettes numériques. Remarquez que, après x1
est réglé, x0
La valeur de l'entreprise ne devrait plus jamais être utilisée, mais elle peut toujours l'être. Si vous l'utilisez accidentellement, ce sera une erreur non détectée.
J'ai réussi à résoudre ce problème en utilisant la monade State :
fooSt x y = execState (do
(x,y) <- get
when (a x) (put (f x, y))
(x,y) <- get
if c x
then put (g x, y)
else put (h x, y)
(x,y) <- get
put (f x, y)
(x,y) <- get
when (a y) (put (x, f y))
(x,y) <- get
when (b y) (put (g x, x))) (x,y)
De cette façon, le besoin de suivi des balises disparaît, de même que le risque d'utiliser accidentellement une variable obsolète. Mais maintenant le code est verbeux et beaucoup plus difficile à comprendre, principalement à cause de la répétition de (x,y) <- get
.
Donc : quelle est une manière plus lisible, plus élégante et plus sûre d'exprimer ce modèle ? ?