Ich würde sagen, das Beste, was wir fragen sollten, ist nicht, wie wir es nennen würden, sondern wie wir ein solches Stück Code analysieren würden . Und meine erste Schlüsselfrage bei einer solchen Analyse wäre:
- Hängt die Nebenwirkung vom Argument der Funktion oder vom Ergebnis der Nebenwirkung ab?
- Nein: Die "wirksame Funktion" kann zu einer reinen Funktion, einer wirksamen Aktion und einem Mechanismus zu deren Kombination umgestaltet werden.
- Ja: Die "effektive Funktion" ist eine Funktion, die ein monadisches Ergebnis erzeugt.
Dies ist in Haskell einfach zu veranschaulichen (und dieser Satz ist nur ein halber Witz). Ein Beispiel für den "Nein" -Fall wäre:
double :: Num a => a -> IO a
double x = do
putStrLn "I'm doubling some number"
return (x*2)
In diesem Beispiel hat die Aktion, die wir ausführen (die Zeile drucken "I'm doubling some number"
), keinen Einfluss auf die Beziehung zwischen x
und dem Ergebnis. Dies bedeutet, dass wir es auf diese Weise umgestalten können (unter Verwendung der Applicative
Klasse und ihres *>
Operators), was zeigt, dass die Funktion und der Effekt tatsächlich orthogonal sind:
double :: Num a => a -> IO a
double x = action *> pure (function x)
where
-- The pure function
function x = x*2
-- The side effect
action = putStrLn "I'm doubling some number"
In diesem Fall würde ich persönlich sagen, dass Sie eine reine Funktion herausrechnen können. Bei vielen Haskell-Programmen geht es darum, wie man die reinen Teile aus dem effektiven Code herausrechnet.
Ein Beispiel für die Art "Ja", bei der der reine und der wirksame Teil nicht orthogonal sind:
double :: Num a => a -> IO a
double x = do
putStrLn ("I'm doubling the number " ++ show x)
return (x*2)
Nun hängt die Zeichenfolge, die Sie drucken, vom Wert von ab x
. Der Funktionsteil (multiplizieren x
mit zwei) hängt jedoch überhaupt nicht vom Effekt ab, sodass wir ihn trotzdem ausklammern können:
logged :: (a -> b) -> (a -> IO x) -> IO b
logged function logger a = do
logger a
return (function a)
double x = logged function logger
where function = (*2)
logger x putStrLn ("I'm doubling the number " ++ show x)
Ich könnte weitere Beispiele nennen, aber ich hoffe, dies ist genug, um den Ausgangspunkt zu verdeutlichen: Man nennt es nicht "etwas", man analysiert, wie die reinen und die wirksamen Teile zusammenhängen und faktorisiert sie, wenn es so ist zu Ihrem Vorteil.
Dies ist einer der Gründe, warum Haskell seine Monad
Klasse so ausgiebig nutzt . Monaden sind unter anderem ein Werkzeug, um diese Art von Analyse und Refactoring durchzuführen.