To jest dokładnie to, co jest dla Reader monad:
monady Reader (zwany także środowisko monada). Reprezentuje obliczenia , które mogą odczytywać wartości ze środowiska współużytkowanego, przekazywać wartości od funkcji do funkcji i wykonywać sub-obliczenia w zmodyfikowanym środowisku .
Jak zauważa sjoerd, monada daje więcej energii, niż trzeba, ale można nadal używać Monada Reader dla tego problemu bez tyle wpisując do
:
import qualified Data.Map as M
import Control.Applicative ((<$>), (<*>))
import Control.Monad.Reader
data Expr = Const Bool
| Var Char
| Not Expr
| And Expr Expr
| Or Expr Expr
| Xor Expr Expr
Wystarczy umieścić swój typ środowiska jako pierwszy argument konstruktora typu Reader
, a pierwotny typ wyniku jako drugi.
eval' :: Expr -> Reader (M.Map Char Bool) Bool
Zamiast c
jako wartość przypadku Const
użyć return
podnieść go do monady:
eval' (Const c) = return c
Kiedy trzeba środowisko dla patrząc wartość zmiennej użytkowania ask
. Korzystanie do
notacji, można napisać sprawę Var
tak:
eval' (Var v) = do values <- ask
return (M.findWithDefault False v values)
myślę, że ładniejsze, choć w użyciu fmap
aka <$>
:
eval' (Var v) = M.findWithDefault False v <$> ask
Podobnie, jednoskładnikowa not
można fmap
ped ponad Wynik rekursji:
eval' (Not x) = not <$> eval' x
Fina lly, wystąpienie Reader Applicative sprawia przypadki binarne przyjemne:
eval' (And a b) = (&&) <$> eval' a <*> eval' b
eval' (Or a b) = (||) <$> eval' a <*> eval' b
eval' (Xor a b) = (/=) <$> eval' a <*> eval' b
Następnie, aby dostać to wszystko się zaczęło, oto pomocnik stworzyć wstępną środowiska i uruchomić obliczenia:
eval :: Expr -> [(Char,Bool)] -> Bool
eval exp env = runReader (eval' exp) (M.fromList env)
Nie tylko jest to prostsze, ale prawdopodobnie również bardziej wydajne. Uważam, że nazywa się to statyczną transformacją argumentów. –