Skip to content

Instantly share code, notes, and snippets.

@HirotoShioi
Last active November 7, 2019 00:32
Show Gist options
  • Save HirotoShioi/78abbb622988b00c54924435dcb056ff to your computer and use it in GitHub Desktop.
Save HirotoShioi/78abbb622988b00c54924435dcb056ff to your computer and use it in GitHub Desktop.
モナド変換子(その1: 基本) ref: https://qiita.com/HirotoShioi/items/8a6107434337b30ce457
someFun :: Env -> IO ()
...
someHandling :: Env -> Int -> IO Int
...
-- Somedataを処理する関数
someFun :: SomeData -> Either String SomeData
....
-- SomeDataからIntを算出する関数
someEither :: SomeData -> Either String Int
eval :: Expr -> Int
eval (Lit n) = n
eval (Add e1 e2) = eval e1 + eval e2
λ: eval (Add (Add (Lit 3) (Lit 4)) (Lit 10))
17
data Expr =
Lit Int
| Add Expr Expr
| Div Expr Expr
eval :: Expr -> Int
eval (Lit n) = n
eval (Add e1 e2) = eval e1 + eval e2
eval (Div e1 e2) = eval e1 `div` eval e2
λ:eval (Div (Lit 10) (Lit 0))
*** Exception: divide by zero
eval :: Expr -> Either String Int
eval (Lit n) = pure n
eval (Add e1 e2) = (+) <$> eval e1 <*> eval e2
eval (Div e1 e2) = do
v1 <- eval e1
v2 <- eval e2
if v2 == 0
then Left "division by 0"
else return (v1 `div` v2)
eval :: Expr -> ??? Int
eval :: (Lit n) = pure n
eval :: (Add e1 e2) = (+) <$> eval e1 <*> eval e2
eval :: Expr -> Identity Int
eval (Lit n) = pure n
eval (Add e1 e2) = (+) <$> eval e1 <*> eval e2
λ: runIdentity $ eval (Add (Lit 10) (Lit 7))
17
eval :: Expr -> ExceptT String Identity Int
eval (Lit n) = pure n
eval (Add e1 e2) = (+) <$> eval e1 <*> eval e2
eval (Div e1 e2) = do
v1 <- eval e1
v2 <- eval e2
if v2 == 0
then throwError "division by 0"
else return (v1 `div` v2)
newtype ErrorState a = ErrorState { runErrorState :: s -> Either String (a, s) }
run :: Expr -> Either String Int
run expr = runIdentity (runExceptT (eval expr))
data Expr =
Lit Int
| Add Expr Expr
| Div Expr Expr
| Var String
type Env = Map String Int
eval :: Expr -> ReaderT Env (ExceptT String Identity) Int
type Eval = ReaderT Env (ExceptT String Identity) Int
-- or
newtype Eval a = Eval (ReaderT Env (ExceptT String Identity) a)
type Message = String
newtype Message = Message String
someFun :: Message -> Maybe Char
-- Why?
evilFun :: Message -> Char
evilFun = head
newtype Message = Message String
-- Stringとは全く別の型クラスインスタンスが実装可能
instance Show Message where
show msg = "New typeclass instance for show" <> show msg
newtype Eval a = Eval (ExceptT String Identity) a
deriving (???)
instance Monad (ErrorState e) where
return a = ErrorState $ \s -> Right (a, s)
m >>= cont = ErrorState $ \s -> case runErrorState m s of
(Left e) -> Left e
(Right (a, s')) -> runErrorState (cont a) s'
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Quantity = Quantity Int
deriving (Eq, Ord, Num, Show)
a = Quantity 2
b = Quantity 6
totalQuantity :: Quantity
totalQuantity = a + b
-- Quantity 8
class (Monad m) => MonadReader r m | m -> r where
ask :: m r
local :: (r -> r) -> m a -> m a
instance (Monad m) => MonadReader r (ReaderT r m) where
ask = ReaderT return
local f m = ReaderT $ \r -> runReaderT m (f r)
newtype App a = App (ReaderT [Int] (Either String) a)
deriving (Functor
, Applicative
, Monad
, MonadReader [Int])
newtype Eval a = Eval (ReaderT Env (ExceptT String Identity) a)
deriving (Functor
, Applicative
, Monad
, MonadReader Env
, MonadError String)
eval :: Expr -> Eval Int
runEval :: Eval a -> Env -> Either String a
runEval (Eval m) env = runIdentity (runExceptT (runReaderT m env))
eval (Var x) = do
env <- ask
case M.lookup x env of
Nothing -> throwError $ "Variable not found: " <> show x
Just num -> pure num
λ: runEval (eval (Add (Var "x") (Lit 10))) (singleton "x" 10)
Right 20
data Expr =
Lit Int
| Add Expr Expr
| Div Expr Expr
| Var String
| Seq Expr Expr
| Assign String Expr
-- Before (ReaderT)
newtype Eval a = Eval (ReaderT Env (ExceptT String Identity) a)
deriving (Functor, Applicative, Monad, MonadReader Env, MonadError String)
-- After (StateT)
newtype Eval a = Eval (StateT Env (ExceptT String Identity) a)
deriving (Functor, Applicative, Monad, MonadState Env, MonadError String)
instance Functor (ErrorState e) where
fmap = liftM
instance Applicative (ErrorState e) where
pure = return
(<*>) = ap
-- Before (ReaderT)
runEval :: Eval a -> Env -> Either String a
runEval (Eval m) env = runIdentity (runExceptT (runReaderT m env))
-- After (StateT)
runEval :: Eval a -> Env -> Either String a
runEval (Eval m) env = runIdentity (runExceptT (evalStateT m env))
eval (Var x) = do
env <- get -- ask
case M.lookup x env of
Nothing -> throwError $ "Variable not found: " <> show x
Just num -> pure num
eval (Seq e1 e2) = e1 >> e2
eval (Assign x e) = do
v <- eval e
modify (M.insert x v)
return v
program :: Expr
program = Assign "x" (Lit 10)
`Seq` Assign "x" (Div (Var "x") (Lit 2))
`Seq` Add (Var "x") (Lit 1)
let x = 10;
x = x / 2;
console.log (x + 1);
λ: runEval (eval program) empty
Right 6
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Interpreter where
import Control.Monad.Identity
import Control.Monad.Except
import Control.Monad.State
import Data.Map
import qualified Data.Map as M
import Data.Monoid ((<>))
type Env = Map String Int
data Expr =
Lit Int
| Add Expr Expr
| Div Expr Expr
| Var String
| Assign String Expr
| Seq Expr Expr
newtype Eval a = Eval (StateT Env (ExceptT String Identity) a)
deriving (Functor, Applicative, Monad, MonadState Env, MonadError String)
eval :: Expr -> Eval Int
eval (Lit n) = pure n
eval (Add e1 e2) = (+) <$> eval e1 <*> eval e2
eval (Div e1 e2) = doDiv e1 e2
eval (Var x) = varLookup x
eval (Seq e1 e2) = eval e1 >> eval e2
eval (Assign x e) = varSet x e
doDiv :: Expr -> Expr -> Eval Int
doDiv e1 e2 = do
v1 <- eval e1
v2 <- eval e2
if v2 == 0
then divByZeroError
else return (v1 `div` v2)
divByZeroError :: Eval a
divByZeroError = throwError "Division by 0"
varLookup :: String -> Eval Int
varLookup x = do
env <- get
case M.lookup x env of
Nothing -> unknownVar x
Just num -> return num
varSet :: String -> Expr -> Eval Int
varSet x e = do
v <- eval e
modify (M.insert x v)
return v
unknownVar :: String -> Eval a
unknownVar x = throwError $ "Variable not found: " <> show x
runEval :: Eval a -> Env -> Either String a
runEval (Eval m) env = runIdentity (runExceptT (evalStateT m env))
program :: Expr
program = Assign "x" (Lit 10)
`Seq` Assign "x" (Div (Var "x") (Lit 2))
`Seq` Add (Var "x") (Lit 1)
λ: runEval (eval (Add (Lit 10) (Get)) empty
Please enter a number
10
Right 20
get :: ErrorState s s
get = ErrorState $ \s -> Right (s, s)
put :: s -> ErrorState s ()
put = ErrorState $ \_ -> Right ((), s)
modify :: (s -> s) -> ErrorState s ()
modify f = get >>= \s -> put (f s)
throwError :: String -> ErrorState s a
throwError str = ErrorState $ \_ -> Left e
addEven :: Int -> ErrorState Int ()
addEven num = if odd num
then throwError $ "Invalid number: " ++ show num
else modify (+ num)
λ: runErrorState (addEven 10) 10
Right ((),20)
λ: runErrorState (addEven 11) 10
Left "Invalid number: 11"
data Expr =
Lit Int
| Add Expr Expr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment