evalExprExt ::    (uniOp -> Int -> Int)
                  -- ^ How to evaluate unary operators
               -> (binOp -> Int -> Int -> Int)
                  -- ^ How to evaluate binary operators
               -> (expr -> Int)
                  -- ^ How to evaluate expressions
               -> ExpressionExt uniOp binOp expr
                  -- ^ Expression
               -> Int
                  -- ^ Evaluation returns some Int
evalExprExt evalUniOp' evalBinOp' evalExpr' expr =
  case expr of
    ExprUniOp op arg -> evalUniOp' op (evalExpr' arg)
    ExprBase base    -> evalExpr evalBinOp' evalExpr' base
--


-- | Evaluate a `BinOpExt` value. Note that this is simply applying `evalMul`,
--   `evalDiv`, and `evalBinOp` to the `BinOpExt` ADT.
evalExprExt ::    (uniOp -> Int -> Int)
                  -- ^ How to evaluate unary operators
               -> (binOp -> Int -> Int -> Int)
                  -- ^ How to evaluate binary operators
               -> (expr -> Int)
                  -- ^ How to evaluate expressions
               -> ExpressionExt uniOp binOp expr
                  -- ^ Expression
               -> Int
evalExprExt evalUniOp' evalBinOp' evalExpr' expr =
  case expr of
    ExprUniOp op arg -> evalUniOp' op (evalExpr' arg)
    ExprBase base    -> evalExpr evalBinOp' evalExpr' base
--


-- | Evaluate a `BinOpExt` value. Note that this is simply applying `evalMul`,
--   `evalDiv`, and `evalBinOp` to the `BinOpExt` ADT.
evalExprExt evalUniOp' evalBinOp' evalExpr' expr =
  case expr of
    ExprUniOp op arg -> evalUniOp' op (evalExpr' arg)
    ExprBase base    -> evalExpr evalBinOp' evalExpr' base
--


-- | Evaluate a `BinOpExt` value. Note that this is simply applying `evalMul`,
--   `evalDiv`, and `evalBinOp` to the `BinOpExt` ADT.