apply_ast ast@(MalList _ _) env = do
    el <- eval_ast ast env
    case el of
         (MalList ((Func (Fn f) _) : rest) _) ->
            f $ rest
         (MalList ((MalFunc {ast=ast, env=fn_env, params=(MalList params Nil)}) : rest) _) -> do
            fn_env1 <- liftIO $ env_new $ Just fn_env
            fn_env2 <- liftIO $ env_bind fn_env1 params rest
            eval ast fn_env2
         el ->
            throwStr $ "invalid apply: " ++ (show el)
apply_ast ast@(MalList _ _) env = do
    el <- eval_ast ast env
    case el of
         (MalList ((Func (Fn f) _) : rest) _) ->
            f $ rest
         (MalList ((MalFunc {ast=ast, env=fn_env, params=(MalList params Nil)}) : rest) _) -> do
            fn_env1 <- liftIO $ env_new $ Just fn_env
            fn_env2 <- liftIO $ env_bind fn_env1 params rest
            eval ast fn_env2
         el ->
            throwStr $ "invalid apply: " ++ (show el)
apply_ast ast@(MalList _ _) env = do
    el <- eval_ast ast env
    case el of
         (MalList ((Func (Fn f) _) : rest) _) ->
            f $ rest
         (MalList ((MalFunc {ast=ast, env=fn_env, params=(MalList params Nil)}) : rest) _) -> do
            fn_env1 <- liftIO $ env_new $ Just fn_env
            fn_env2 <- liftIO $ env_bind fn_env1 params rest
            eval ast fn_env2
         el ->
            throwStr $ "invalid apply: " ++ (show el)