user6262188
user6262188

Reputation:

How to avoid `case` when handling `Maybe`

I am writing interpreter in Haskell and I have map: name_of_function -> defintion of function.

Map String Defun

My interpreter monad:

type InterpreterMonad  = StateT (EnvFun, (Stack EnvEval)) (ErrorT String IO ) ()

And as you can see "last" monad is ErroT.

Now, when I would like handle calling functions:

   handleFCall :: Stmt -> InterpreterMonad
    handleFCall (VarName name) args =  get >>= \(envFun, contextStack) -> case (Map.lookup (VarName name) envFun) of 
        Nothing -> throwError "Err"
        (Just x) -> DoSthOther

And as you can see I have to use case. However I am using >>= so I would like to avoid case of here. But, Map.lookup return Nothing on fail and I would like to add my error message.

I have no experience in Haskell so I don't know how to deal with it . Morever, every criticism to my code is welcome.

Cheers.

Upvotes: 2

Views: 1532

Answers (2)

Luis Casillas
Luis Casillas

Reputation: 30227

The errors package has the following operations in Control.Error.Util (and reexported from Control.Error):

-- You can turn `Maybe` into `Either` by supplying a constant to use
-- for the `Left` case.
note :: a -> Maybe b -> Either a b

-- Ditto for `MaybeT` and `ExceptT`
noteT :: Monad m => a -> MaybeT m b -> ExceptT a m b

-- And there are useful shortcuts for other combinations
failWith :: Applicative m => e -> Maybe a -> ExceptT e m a

failWith in particular looks like it would fit quite nicely here, something like this expression would do the trick.

failWith "Err" (Map.lookup (VarName name) envFun)

Note that the ErrorT transformer that you're using in your example has been deprecated in favor of ExceptT (as the signatures above are using), so you'd have to change your code accordingly to use the errors package. On the other hand these functions are simple enough that you can just bake your own if you so prefer.

Upvotes: 2

bheklilr
bheklilr

Reputation: 54058

There's nothing wrong with using a case statement, although reformatted with do notation your function would look more like

handleFCall (VarName name) args = do
    (envFun, contextStack) <- get
    case Map.lookup (VarName name) envFun of
        Nothing -> throwError "Err"
        Just x -> doSthOther x

Which would be more familiar to other Haskell programmers. However, if you really want to avoid using a case, just use the maybe function:

handleFCall (VarName name) args = do
    (envFun, contextStack) <- get
    maybe (throwError "Err") doSthOther $ Map.lookup (VarName name) envFun

Or with >>=

handleFCall (VarName name) args
    = get >>= 
        (\(envFun, contextStack) ->
            maybe (throwError "Err") doSthOther
                $ Map.lookup (VarName name) envFun
        )

But personally I find the case statement more readable.

Upvotes: 4

Related Questions