evaluateFunction :: Ord a => (a -> Either b ([a], [b] -> b)) -> a -> b
evaluateFunction f n = (M.!) (eval M.empty n) n where
  eval cache m = case M.lookup m cache of
    Just _  -> cache
    Nothing ->
      case f m of
        Left v  -> M.insert m v cache
        Right (ks, reducer) -> M.insert m v cache' where
          cache' = foldl eval cache ks
          v = reducer $ map ((M.!) cache') ks
evaluateFunction :: Ord a => (a -> Either b ([a], [b] -> b)) -> a -> b
evaluateFunction f n = (M.!) (eval M.empty n) n where
  eval cache m = case M.lookup m cache of
    Just _  -> cache
    Nothing ->
      case f m of
        Left v  -> M.insert m v cache
        Right (ks, reducer) -> M.insert m v cache' where
          cache' = foldl eval cache ks
          v = reducer $ map ((M.!) cache') ks
evaluateFunction f n = (M.!) (eval M.empty n) n where
  eval cache m = case M.lookup m cache of
    Just _  -> cache
    Nothing ->
      case f m of
        Left v  -> M.insert m v cache
        Right (ks, reducer) -> M.insert m v cache' where
          cache' = foldl eval cache ks
          v = reducer $ map ((M.!) cache') ks