listEquals :: (LispVal -> LispVal -> ThrowsError LispVal)
           -> LispVal -> LispVal -> ThrowsError LispVal
listEquals cmp (List xs) (List ys) =
    return $ Bool $ (length xs == length ys)  && (all eqvPair $ zip xs ys)
  where eqvPair (x, y) = case cmp x y of
                           Left err -> False
                           Right (Bool val) -> val

{-
-- TODO: Ensure `else` can only appear in the last place, would require manual recursion
-- TODO: NumArgs with range of arguments, e.g. > 1
cond :: Env -> [LispVal] -> ThrowsError LispVal
cond env []      = throwError $ NumArgs 1 []
cond env clauses = do
  -- if result is [Nothing Nothing (Just x) ...], we want (Just x)
  results <- mapM (clause env) clauses
  case foldl1 (\r c -> case r of Nothing -> c; Just x -> Just x) results of
    Nothing -> throwError $ BadSpecialForm "cond with no matching clause near" (head clauses)
    Just x -> return x
--cond clauses = (mapM evalClause clauses) >>= return . last
-- cond clauses = foldl (\p e -> case p of Left erreval e) (return $ Bool True) clauses

clause :: Env -> LispVal -> IOThrowsError (Maybe LispVal)
clause env (List ((Atom "else") : exprs)) = clause env (List (Bool True : exprs))
clause env (List x@[test, Atom "=>", expr]) = throwError $ BadSpecialForm "not implemented yet" (Atom "=>")
clause env (List (test : exprs)) = do
  testResult <- eval env test
  case testResult of
    Bool True -> clauseExpr exprs >>= return . Just
    Bool False -> return Nothing
    otherwise -> throwError $ TypeMismatch "bool" otherwise

clauseExpr :: Env -> [LispVal] -> IOThrowsError LispVal
clauseExpr env exprs = mapM (eval env) exprs >>= return . last
-}
listEquals :: (LispVal -> LispVal -> ThrowsError LispVal)
           -> LispVal -> LispVal -> ThrowsError LispVal
listEquals cmp (List xs) (List ys) =
    return $ Bool $ (length xs == length ys)  && (all eqvPair $ zip xs ys)
  where eqvPair (x, y) = case cmp x y of
                           Left err -> False
                           Right (Bool val) -> val

{-
-- TODO: Ensure `else` can only appear in the last place, would require manual recursion
-- TODO: NumArgs with range of arguments, e.g. > 1
cond :: Env -> [LispVal] -> ThrowsError LispVal
cond env []      = throwError $ NumArgs 1 []
cond env clauses = do
  -- if result is [Nothing Nothing (Just x) ...], we want (Just x)
  results <- mapM (clause env) clauses
  case foldl1 (\r c -> case r of Nothing -> c; Just x -> Just x) results of
    Nothing -> throwError $ BadSpecialForm "cond with no matching clause near" (head clauses)
    Just x -> return x
--cond clauses = (mapM evalClause clauses) >>= return . last
-- cond clauses = foldl (\p e -> case p of Left erreval e) (return $ Bool True) clauses

clause :: Env -> LispVal -> IOThrowsError (Maybe LispVal)
clause env (List ((Atom "else") : exprs)) = clause env (List (Bool True : exprs))
clause env (List x@[test, Atom "=>", expr]) = throwError $ BadSpecialForm "not implemented yet" (Atom "=>")
clause env (List (test : exprs)) = do
  testResult <- eval env test
  case testResult of
    Bool True -> clauseExpr exprs >>= return . Just
    Bool False -> return Nothing
    otherwise -> throwError $ TypeMismatch "bool" otherwise

clauseExpr :: Env -> [LispVal] -> IOThrowsError LispVal
clauseExpr env exprs = mapM (eval env) exprs >>= return . last
-}
listEquals cmp (List xs) (List ys) =
    return $ Bool $ (length xs == length ys)  && (all eqvPair $ zip xs ys)
  where eqvPair (x, y) = case cmp x y of
                           Left err -> False
                           Right (Bool val) -> val

{-
-- TODO: Ensure `else` can only appear in the last place, would require manual recursion
-- TODO: NumArgs with range of arguments, e.g. > 1
cond :: Env -> [LispVal] -> ThrowsError LispVal
cond env []      = throwError $ NumArgs 1 []
cond env clauses = do
  -- if result is [Nothing Nothing (Just x) ...], we want (Just x)
  results <- mapM (clause env) clauses
  case foldl1 (\r c -> case r of Nothing -> c; Just x -> Just x) results of
    Nothing -> throwError $ BadSpecialForm "cond with no matching clause near" (head clauses)
    Just x -> return x
--cond clauses = (mapM evalClause clauses) >>= return . last
-- cond clauses = foldl (\p e -> case p of Left erreval e) (return $ Bool True) clauses

clause :: Env -> LispVal -> IOThrowsError (Maybe LispVal)
clause env (List ((Atom "else") : exprs)) = clause env (List (Bool True : exprs))
clause env (List x@[test, Atom "=>", expr]) = throwError $ BadSpecialForm "not implemented yet" (Atom "=>")
clause env (List (test : exprs)) = do
  testResult <- eval env test
  case testResult of
    Bool True -> clauseExpr exprs >>= return . Just
    Bool False -> return Nothing
    otherwise -> throwError $ TypeMismatch "bool" otherwise

clauseExpr :: Env -> [LispVal] -> IOThrowsError LispVal
clauseExpr env exprs = mapM (eval env) exprs >>= return . last
-}