Function with excessively complex "where" clause
It is a matter of taste but I think reduce will be a bit more cleaner with case.

reduce exp = case exp of
  Literal{}             -&gt; exp
  Variable _ (Just var) -&gt; Literal var
  Variable _ Nothing    -&gt; exp
  FCall f a b
    | isDefined f ma mb -&gt; matchLiterals ma mb
    | otherwise         -&gt; 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 -&gt; b -&gt; c) -&gt; (a -&gt; b) -&gt; a -&gt; a -&gt; c


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

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