Symbolic Math in Haskell -


import control.monad (liftm2)  infixl 4 :+:, :-:  infixl 5 :*:, :/:  data expr  = const           | (expr a) :+: (expr a)           | (expr a) :-: (expr a)          | (expr a) :*: (expr a)          | (expr a) :/: (expr a)          deriving (show, eq)  evalexpr (const a) =  evalexpr (a :+: b) = liftm2 (+) (evalexpr a) (evalexpr b) evalexpr (a :-: b) = liftm2 (-) (evalexpr a) (evalexpr b) evalexpr (a :*: b) = liftm2 (*) (evalexpr a) (evalexpr b) evalexpr (a :/: b) = if (evalexpr b) == 0          nothing           else liftm2 (/) (evalexpr a) (evalexpr b) 

this symbolic representation of math , evaluation function. have problem limited knowledge of monads , maybe type problem arises me wanting return nothing value if there division 0 in evaluation function. many different reasons when try run evalexpr (const 3) or more complicated fails on runtime. there missing?

the version think want is:

import control.monad (liftm2)  infixl 4 :+:, :-:  infixl 5 :*:, :/:  data expr  = const           | (expr a) :+: (expr a)           | (expr a) :-: (expr a)          | (expr a) :*: (expr a)          | (expr a) :/: (expr a)          deriving (show, eq)  evalexpr :: (eq a, num a, fractional a) => (expr a) -> maybe evalexpr (const a) = return  evalexpr (a :+: b) = liftm2 (+) (evalexpr a) (evalexpr b) evalexpr (a :-: b) = liftm2 (-) (evalexpr a) (evalexpr b) evalexpr (a :*: b) = liftm2 (*) (evalexpr a) (evalexpr b) evalexpr (a :/: b) = if (evalexpr b) == return 0         nothing          else liftm2 (/) (evalexpr a) (evalexpr b) 

the changes using correct monadic (in case maybe) type in const case (return a instead of a) , when check 0 (if (evalexpr b) == return 0 rather if (evalexpr b) == 0).

(not both have used just rather return because maybe monad using.)

by fixing type of evalexpr able work out problems more easily. use of liftm2 makes haskell expressions quite general, hence many different versions of either compiled , didn't work expected or failed compile confusing messages. once type fixed compiler told me straight away const case , == expression causing problems.

you may interested in using applicative functions <$> , <*> instead of liftm2, these can used no matter how many arguments there are:

import control.applicative ((<$>), (<*>))  ...  evalexpr :: (eq a, num a, fractional a) => (expr a) -> maybe evalexpr (const a) = return  evalexpr (a :+: b) = (+) <$> (evalexpr a) <*> (evalexpr b) evalexpr (a :-: b) = (-) <$> (evalexpr a) <*> (evalexpr b) evalexpr (a :*: b) = (*) <$> (evalexpr a) <*> (evalexpr b) evalexpr (a :/: b) = if (evalexpr b) == return 0         nothing          else (/) <$> (evalexpr a) <*> (evalexpr b) 

if want lift function of 3 arguments, triad say, is

triad <$> arg1 <*> arg2 <*> arg3 

look applicative functors more info. there description here.

update

@gallais made point style of division case. wasn't going mention in interests of clarity, how it:

evalexpr :: (eq a, num a, fractional a) => (expr a) -> maybe evalexpr (const a) = return  evalexpr (a :+: b) = (+) <$> (evalexpr a) <*> (evalexpr b) evalexpr (a :-: b) = (-) <$> (evalexpr a) <*> (evalexpr b) evalexpr (a :*: b) = (*) <$> (evalexpr a) <*> (evalexpr b) evalexpr (a :/: b) = (/) <$> (evalexpr a) <*> (failon 0 $ evalexpr b)  failon x = case of     x -> nothing     _      -> 

or more general version taking advantage of monadzero class , taking function:

... evalexpr (a :/: b) = (/) <$> (evalexpr a) <*> (failon (==0) $ evalexpr b)  failon f =     b <- fmap f     if b mzero else 

there other monadic helper functions useful in cases this, such guard, when , unless, see here.


Comments

Popular posts from this blog

c++ - QTextObjectInterface with Qml TextEdit (QQuickTextEdit) -

javascript - angular ng-required radio button not toggling required off in firefox 33, OK in chrome -

xcode - Swift Playground - Files are not readable -