Reputation:
I would like to understand how to deal with stack of transformators in do section. It is also about error handling.
Namely, I will show you my problem in my case.
I have some funcion:
foo :: Int -> String -> IO (Either String (Value, Integer))
Body of foo
is not important here.
ef :: evalExpr :: Int -> String -> IO (Either String (Value, Integer))
Now,
f :: Integer -> String -> StateT Integer (ReaderT Integer (ErrorT String IO)) ()
f i s = do
vl <- lift $ lift $ lift $ ef i s
....
return ()
ef
can call throwError. Also it can return (Value, String)
.
I would like to be able (....
) do something with Right value (so (Value, String)
). However, I can't do it. It is my permanent problem.
Idea is following:
When ef
called throwError
then foo
should propagate the same error (so Left String
). Neverhteless, when ef
return Right Value (so (Value, String))
I would like to use it and on this base make decision what should return foo
- throwError
or (Value, ())
I know about fmap
and <*>
.
Can you help me, please ?
Upvotes: 1
Views: 32
Reputation: 39370
The function:
ef :: Int -> String -> IO (Either String (Value, Integer))
Can only throwError
an IOException
:
instance MonadError IOException IO where
throwError = ioError
catchError = catch
Otherwise, it's just an IO
function returning Either String (Value, Integer)
.
Given that function, to act on its value you need to execute it in an IO
-capable context, and then simply use the resulting value:
callEf :: IO ()
callEf = do
result <- ef ...
case result of
Left s -> -- do something with the String
Right (v, i) -> -- do something with the tuple
return ()
-- ^ you can also obviously produce the value above,
-- if you want something more complicated than ()
This doesn't change if you're in a monad stack. In your function f
, calling lift
three times give you an IO
context. After that, you can do whatever you want with the resulting value, including any operation from the stack, like throwError
(after two lifts) or put
(with no lifts).
Alternatively, refer to my answer to your previous question to get rid of the lift
s.
If you want the error propagation to happen automatically from Either
to ErrorT
, it would be reasonably simple, just like above:
eitherToError :: Either e v -> ErrorT e m v
eitherToError f = case f of
Left err -> throwError err
Right val -> return val
Which you can easily search for in Hayoo, resulting in a very similar implementation in some library.
All that being said, it seems that ErrorT
is deprecated in favor of Control.Monad.Trans.Except
.
Upvotes: 2