# Function with excessively complex "where" clause

I'm writing a program to differentiate functions (perform basic calculus) in Haskell. The program works, but there's one function that looks very ugly to me:

--some prerequisite definitions

--a mathematical value is either...
data Value =
Literal Double                     --a number, such as 4, -17.5, or 2.33e27
| Variable Symbol (Maybe Double)   --a variable, which may or may not have a known value
| FCall                            --a function call, consisting of a function and two arguments
{
func :: Function,
arg1 :: Value,
arg2 :: Value
}
deriving (Eq)

--simplifies a mathematical value into a simpler form.

reduce :: Value -> Value
reduce val@(Literal _)          = val               --literals cannot be simplified
reduce (Variable _ (Just val))  = Literal val       --bound variables can be simplified to literals
reduce var@(Variable _ Nothing) = var               --unbound variables cannot be simplified
reduce fc@(FCall f a b)         =                   --simplifying functions is messy...
if isDefined f ma mb
then matchLiterals ma mb
else retval
where ra = reduce a  --reduce simplifies a Value to a Value
rb = reduce b
ma = eval ra   --eval converts known values to Doubles and unknown Values to Nothing
mb = eval rb
retval = FCall f ra rb  --returned in case of failure
matchLiterals (Just da) (Just db) = Literal $(impl f) da db matchLiterals _ _ = matchCase$ findCase (cases f) ra rb
matchCase (Just sc) = evalCase sc ra rb
matchCase Nothing = retval


The multitude of local functions defined in the where clause seems excessive, but I can't find a better way to handle the flow of execution. I've tried to break this up into several smaller functions, but since most of these can fail (evaluate to Nothing), I still have to use pattern matching to handle the results of these functions.

The flow of logic goes like this:

if the function is defined for the given arguments:
if the arguments have definite known values:
evaluate and return the function call
else:
look for special cases (adding 0, multiplying/diving by 1, etc.)
if a special case was found:
evaluate and return the result of applying the special case to the arguments
else:
return another FCall with possibly simplified arguments

else:
return another FCall with possibly simplified arguments


It's possible I'm fretting over nothing (hah) and this code is perfectly fine, but it seems that this is not the optimal way to handle these conditions. Can anyone offer any advice about how I might better structure this code?

It is a matter of taste but I think reduce will be a bit more cleaner with case.

reduce exp = case exp of
Literal{}             -> exp
Variable _ (Just var) -> Literal var
Variable _ Nothing    -> exp
FCall f a b
| isDefined f ma mb -> matchLiterals ma mb
| otherwise         -> FCall f ra rb
where ra = reduce a
rb = reduce b
ma = eval ra
mb = eval rb


Literal{} is special pattern syntax that will still work even if you add more arguments to Literal.

To reduce duplication (just a bit) in where, you can use on function from Data.Function

on :: (b -> b -> c) -> (a -> b) -> a -> a -> c


it is like (.) but allows to apply function to both arguments

(ra,rb) = on (,) reduce a b
(ma,mb) = on (,) eval ra rb

• Also, on is often used infix, e.g f on g rather than on f g Commented May 2, 2014 at 21:36