module Main where
--
-- In order to use "do" notation, we need to declare instances
-- of the Monad typeclass. In order to do that we must jump through
-- some hoops:
-- * We need a name for the datatype we are using (hence the new "Counter" type)
-- * We also must declare instances of Functor and Applicative
-- (this is something that changed in Haskell 7.10.
-- See https://w...content-available-to-author-only...l.org/Functor-Applicative-Monad_Proposal )
-- (Applicative's "pure" is the same as Monad's "return")
--
-- Integer arithmetic with (+) and (/)
data Term =
| Plus Term Term
| Div Term Term
--
-- Basic evaluator
--
eval (N n) = n
eval (Plus t1 t2) = eval t1 + eval t2
eval
(Div t1 t2
) = eval t1 `
div` eval t2
--
-- Evaluator that counts the number of operations
--
newtype Counter a
= Counter
(Int -> (Int, a
))
unCounter
:: Counter a
-> (Int -> (Int, a
))unCounter (Counter f) = f
-- fmap :: (a -> b) -> Counter a -> Counter b
instance Applicative Counter where
-- pure :: a -> Counter a
-- Counter (a -> b) -> Counter a -> Counter b
instance Monad Counter
where -- (>>=) :: Counter a -> (a -> Counter b) -> Counter b
runC
:: Counter a
-> Int -> (Int, a
)
inc :: Counter ()
evalWithCount
:: Term
-> (Int, Int)evalWithCount t = runC (eval' t) 0
where
eval' :: Term
-> Counter
Int eval' (N n) =
return n
eval' (Plus t1 t2) = do
n1 <- eval' t1
n2 <- eval' t2
inc
eval' (Div t1 t2) = do
n1 <- eval' t1
n2 <- eval' t2
inc
return (n1 `div` n2)
--
-- Evaluator that catches division by zero
--
-- Returns (Ok n) in case of success
-- Returns (Error "Division by zero") otherwise
--
data Result a = Error String | Ok a deriving Show
instance Functor Result where
-- fmap :: (a -> b) -> Result a -> Result b
fmap f r = undefined
instance Applicative Result where
-- pure :: a -> Result a
pure n = undefined
r1 <*> r2 = undefined
instance Monad Result where
-- (>>=) :: Result a -> (a -> Result a) -> Result b
m >>= f = undefined
throw :: String -> Result a
throw s = undefined
evalChecked :: Term -> Result Int
evalChecked (N n) =
return n
evalChecked (Plus t1 t2) = do
n1 <- evalChecked t1
n2 <- evalChecked t2
return (n1 + n2)
evalChecked (Div t1 t2) = do
n1 <- evalChecked t1
n2 <- evalChecked t2
if n2 == 0 then
throw "Division by zero"
else
return (n1 `div` n2)
main = print (undefined::String)