Working in a Coding Dojo today I tried the following
example :: IO ()
example = do input <- getLine
parsed <- parseOnly parser input
...
where parseOnly :: Parser a -> Either String a (from attoparsec) of course the compiler complained that Either .. is not IO .. essentially telling me I am mixing monads.
Of course this can be solved by
case parseOnly parser input of .. -> ..
which is kind of unelegant, I think. Also my guess is that somebody else had this problem earlier and the solution I think is related to monad transformers, but the last bits I cannot piece together.
It also reminded me of liftIO - but this is the other way around I think which solves the problem of lifting an IO action happening inside some surrounding monad (more precisely MonadIO - say for example inside Snap when one wants to print something to stdout while getting some http).
More general this problem seems to be for a Monad m1 and a (different) Monad m2 how can I do something like
example = do a <- m1Action
b <- m2Action
..
You can't, in general. The whole do block has to be one specific monad (because example needs to have some specific type). If you could bind an arbitrary other monad inside that do block, you'd have unsafePerformIO.
Monad transformers allow you to produce one monad combining the kinds of things multiple other monads can do. But you have to decide that all the actions in your do block use the same monad transformer stack to use those, they're not a way to arbitrarily switch monads mid do-block.
Your solution with case only works because you've got a particular known monad (Either) that has a way of extracting values from inside it. Not all monads provide this, so it's impossible to build a general solution without knowing the particular monads involved. That's why the do block syntax doesn't provide such a shortcut.
In general, monad transformers are for this kind of interleaving. You can use ExceptT
example :: IO (Either String ())
example = runExceptT $ do
input <- liftIO getLine
parsed <- parseOnly parser input
...
Note that parseOnly must return ExceptT String IO a for some a. Or better ExceptT String m a for any m. Or if you want parseOnly to return Either String a it's
example :: IO (Either String ())
example = runExceptT $ do
input <- lift getLine
parsed <- ExceptT $ return $ parseOnly parser input
...
But I think all you need is just
eitherToIO :: Either String a -> IO a
eitherToIO (Left s) = error s
eitherToIO (Right x) = return x
parseOnly :: ... -> String -> Either String Int
example :: IO ()
example = do
input <- getLine
parsed <- eitherToIO $ parseOnly parser input
...
You need to make that expression type check; just as in pure code. Here,
... = do a <- act1 -- m1 monad
b <- act2 -- m2 monad
...
de-sugars to:
... = act1 >>= (\a -> act2 >>= \b -> ...)
>>= is of signature:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
the outer bind is specialized with m1, so it expects that the expression inside parenthesis be of type: a -> m1 b, whereas the inner bind is specialized with m2, so the expression inside parenthesis will be of type a -> m2 b:
-- outer bind expects ( \a -> m1 b )
act1 >>= (\a -> act2 >>= \b -> ...)
-- inner bind results ( \a -> m2 b )
for this to type check, you need a function of signature m2 b -> m1 b in between the two; that is what lift does for a certain class of m2 and m1 monads: namely m1 ~ t m2 where t is an instance of MonadTrans:
lift :: (Monad m, MonadTrans t) => m a -> t m a
Related
I'm trying to write an interpreter for a simple embedded scripting language.
The core of it is the eval function, which has the following signature.
type EvalState = () --for later
type EvalResult = State EvalState (IO (Either T.Text Value))
eval :: Expr -> EvalResult
The result type is like this because it is statefull, eval should be able todo IO and it can fail.
There are two datatypes: Expressions and Values and eval converts expression to values. For simple expressions, literals of primitive data types, it's implemented like this:
ok :: Value -> EvalResult
ok val = state (return . Right $ val,)
eval :: Expr -> EvalResult
eval (StrLit t) = ok (StrVal t)
eval (SymLit t) = ok (SymbolVal t)
eval (IntLit i) = ok (IntVal i)
eval (FloatLit f) = ok (FloatVal f)
My problem now is implementing eval for the list literal. Currently my code looks like this:
eval (ListLit elems) = do
elems <- mapM eval elems
do
opts <- sequence elems
return $ fmap (Right . ListVal . V.fromList) (sequence opts)
And this produces the following error:
/home/felix/git/vmail/src/Interpreter/EvalAst.hs:37:13: error:
• Couldn't match type ‘IO’
with ‘StateT EvalState Data.Functor.Identity.Identity’
Expected: StateT
EvalState Data.Functor.Identity.Identity [Either T.Text Value]
Actual: IO [Either T.Text Value]
• In a stmt of a 'do' block: opts <- sequence elems
In a stmt of a 'do' block:
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
In the expression:
do elems <- mapM eval elems
do opts <- sequence elems
fmap (Right . ListVal . V.fromList) (sequence opts)
|
37 | opts <- sequence elems
| ^^^^^^^^^^^^^^
My problem is that I'm not understanding this error. My thinking goes like this: the first do puts me in the State monad, so I shold be able to "extract" the results from mapM eval elems which I expect to be [ IO (Either ...) ] The next do should put me in the IO monad (because that's the next inner type in the result type), so I should be able to extract IO values, and as far as I understood it, sequence elems should give me a IO [ Either ... ] which I can then extract, so what is my mistake?
Monad do not compose, in general. I think you are being bit by this.
In general, if m and n are monads, we can not write m (n a) for a monadic action that mixes the features of m and n. In general, it could even fail to be a monad.
In your case, you are using something like State A (IO B) hoping to be able to access the state of type A and still do IO, but that's not the case.
Indeed, by definition we have (up to some wrappers):
State a b = a -> (a,b)
| | |-- result
| |-- new state
|-- old state
which in your case State A (IO B) it becomes
State A (IO B) = A -> (A, IO B)
| | |-- result
| |-- new state
|-- old state
Here, we can see that the new state must be generated without doing IO at all! This is a computation which forces the new state and the IO effects to be completely separated. It effectively behaves as if we had two separate functions
A -> A -- state update, no IO here
A -> IO B -- IO depending on the old state
Likely, that's not what you actually want. That is probably something like
A -> IO (A, B)
allowing the new state to be generated after IO is done.
To obtain that type, you can not nest monads, but you need a monad transformer like StateT:
StateT A IO B = A -> IO (A, B)
This is a monad. You will probably need to use lift or liftIO to convert IO actions to this type, but it should behave as desired.
I have the following code snippet from internet:
calculateLength :: LengthMonad Int
calculateLength = do
-- all the IO operations have to be lifted to the IO monad in the monad stack
liftIO $ putStrLn "Please enter a non-empty string: "
s <- liftIO getLine
if null s
then throwError "The string was empty!"
else return $ length s
and could not understand, why the author use liftIO?
What is the purpose of liftIO?
It is defined as follows:
class (Monad m) => MonadIO m where
-- | Lift a computation from the 'IO' monad.
liftIO :: IO a -> m a
Is it possible to lift IO a -> [a]? It looks like natural transformation.
IO operations like getLine, putStrLn "..." only work inside the IO monad. Using them inside any other monad will trigger a type error.
Still, there are many monads M which are defined in terms of IO (e.g. StateT Int IO, and apparently your LengthMonad as well) and because of that they allow IO actions to be converted into M-actions, and executed as such.
However, we need a conversion for each M:
convertIOintoM1 :: IO a -> M1 a
convertIOintoM2 :: IO a -> M2 a
convertIOintoM3 :: IO a -> M3 a
...
Since this is cumbersome, the library defines a typeclass MonadIO having such conversion function, so that all the functions above can be named liftIO instead.
In practice, liftIO is used each time one wants to run IO actions in another monad, provided such monad allows for it.
I want to create a generalized form of IO based on ContT. I created a GADT to represent different IO actions:
data Cmd a where
PutChar :: Char -> Cmd ()
GetChar :: Cmd Char
I wrote a function that transforms these into IO commands for use in the IO monad like so:
continueIO :: Cmd a -> IO a
continueIO (PutChar c) = putChar c
continueIO GetChar = getChar
Here's what a sample usage of this should look like:
echoChar :: (Monad m) => ContT r m (Cmd a)
echoChar = ContT $ \k -> do
c <- k GetChar
k (PutChar c)
It should be run with something like runContT echoChar continueIO. However, the PutChar and GetChar commands conflict in their types. How can I dispatch both types from the same ContT?
P.S. Sorry if anything in this question is awkwardly worded. I'm trying to give myself a challenge here and I don't completely understand what I'm trying to do.
Edit: I am not restricted in my solution, and I do not have to use ContT.
Either your commands all need to be closures that return the same type (such as IO (), in which case you can’t do getChar) or you need to have a polymorphic result type that can hold either IO () or IO Char, and ContinueIO needs to take this as its argument. Then you need to decide what happens when you pass an IO Char to GetChar or nothing to PutChar. (This version could be a Monoid with <> = ContinueIO and mempty = id, and then you could foldMap over any Foldable structure.)
Could someone give a super simple (few lines) monad transformer example, which is non-trivial (i.e. not using the Identity monad - that I understand).
For example, how would someone create a monad that does IO and can handle failure (Maybe)?
What would be the simplest example that would demonstrate this?
I have skimmed through a few monad transformer tutorials and they all seem to use State Monad or Parsers or something complicated (for a newbee). I would like to see something simpler than that. I think IO+Maybe would be simple, but I don't really know how to do that myself.
How could I use an IO+Maybe monad stack?
What would be on top? What would be on bottom? Why?
In what kind of use case would one want to use the IO+Maybe monad or the Maybe+IO monad? Would that make sense to create such a composite monad at all? If yes, when, and why?
This is available here as a .lhs file.
The MaybeT transformer will allow us to break out of a monad computation much like throwing an exception.
I'll first quickly go over some preliminaries. Skip down to Adding Maybe powers to IO for a worked example.
First some imports:
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
Rules of thumb:
In a monad stack IO is always on the bottom.
Other IO-like monads will also, as a rule, always appear on the bottom, e.g. the state transformer monad ST.
MaybeT m is a new monad type which adds the power of the Maybe monad to the monad m - e.g. MaybeT IO.
We'll get into what that power is later. For now, get used to thinking of MaybeT IO as the maybe+IO monad stack.
Just like IO Int is a monad expression returning an Int, MaybeT IO Int is a MaybeT IO expression returning an Int.
Getting used to reading compound type signatures is half the battle to understanding monad transformers.
Every expression in a do block must be from the same monad.
I.e. this works because each statement is in the IO-monad:
greet :: IO () -- type:
greet = do putStr "What is your name? " -- IO ()
n <- getLine -- IO String
putStrLn $ "Hello, " ++ n -- IO ()
This will not work because putStr is not in the MaybeT IO monad:
mgreet :: MaybeT IO ()
mgreet = do putStr "What is your name? " -- IO monad - need MaybeT IO here
...
Fortunately there is a way to fix this.
To transform an IO expression into a MaybeT IO expression use liftIO.
liftIO is polymorphic, but in our case it has the type:
liftIO :: IO a -> MaybeT IO a
mgreet :: MaybeT IO () -- types:
mgreet = do liftIO $ putStr "What is your name? " -- MaybeT IO ()
n <- liftIO getLine -- MaybeT IO String
liftIO $ putStrLn $ "Hello, " ++ n -- MaybeT IO ()
Now all of the statement in mgreet are from the MaybeT IO monad.
Every monad transformer has a "run" function.
The run function "runs" the top-most layer of a monad stack returning
a value from the inside layer.
For MaybeT IO, the run function is:
runMaybeT :: MaybeT IO a -> IO (Maybe a)
Example:
ghci> :t runMaybeT mgreet
mgreet :: IO (Maybe ())
ghci> runMaybeT mgreet
What is your name? user5402
Hello, user5402
Just ()
Also try running:
runMaybeT (forever mgreet)
You'll need to use Ctrl-C to break out of the loop.
So far mgreet doesn't do anything more than what we could do in IO.
Now we'll work on an example which demonstrates the power of mixing
the Maybe monad with IO.
Adding Maybe powers to IO
We'll start with a program which asks some questions:
askfor :: String -> IO String
askfor prompt = do
putStr $ "What is your " ++ prompt ++ "? "
getLine
survey :: IO (String,String)
survey = do n <- askfor "name"
c <- askfor "favorite color"
return (n,c)
Now suppose we want to give the user the ability to end the survey
early by typing END in response to a question. We might handle it
this way:
askfor1 :: String -> IO (Maybe String)
askfor1 prompt = do
putStr $ "What is your " ++ prompt ++ " (type END to quit)? "
r <- getLine
if r == "END"
then return Nothing
else return (Just r)
survey1 :: IO (Maybe (String, String))
survey1 = do
ma <- askfor1 "name"
case ma of
Nothing -> return Nothing
Just n -> do mc <- askfor1 "favorite color"
case mc of
Nothing -> return Nothing
Just c -> return (Just (n,c))
The problem is that survey1 has the familiar staircasing issue which
doesn't scale if we add more questions.
We can use the MaybeT monad transformer to help us here.
askfor2 :: String -> MaybeT IO String
askfor2 prompt = do
liftIO $ putStr $ "What is your " ++ prompt ++ " (type END to quit)? "
r <- liftIO getLine
if r == "END"
then MaybeT (return Nothing) -- has type: MaybeT IO String
else MaybeT (return (Just r)) -- has type: MaybeT IO String
Note how all of the statemens in askfor2 have the same monad type.
We've used a new function:
MaybeT :: IO (Maybe a) -> MaybeT IO a
Here is how the types work out:
Nothing :: Maybe String
return Nothing :: IO (Maybe String)
MaybeT (return Nothing) :: MaybeT IO String
Just "foo" :: Maybe String
return (Just "foo") :: IO (Maybe String)
MaybeT (return (Just "foo")) :: MaybeT IO String
Here return is from the IO-monad.
Now we can write our survey function like this:
survey2 :: IO (Maybe (String,String))
survey2 =
runMaybeT $ do a <- askfor2 "name"
b <- askfor2 "favorite color"
return (a,b)
Try running survey2 and ending the questions early by typing END as a response to either question.
Short-cuts
I know I'll get comments from people if I don't mention the following short-cuts.
The expression:
MaybeT (return (Just r)) -- return is from the IO monad
may also be written simply as:
return r -- return is from the MaybeT IO monad
Also, another way of writing MaybeT (return Nothing) is:
mzero
Furthermore, two consecutive liftIO statements may always combined into a single liftIO, e.g.:
do liftIO $ statement1
liftIO $ statement2
is the same as:
liftIO $ do statement1
statement2
With these changes our askfor2 function may be written:
askfor2 prompt = do
r <- liftIO $ do
putStr $ "What is your " ++ prompt ++ " (type END to quit)?"
getLine
if r == "END"
then mzero -- break out of the monad
else return r -- continue, returning r
In a sense, mzero becomes a way of breaking out of the monad - like throwing an exception.
Another example
Consider this simple password asking loop:
loop1 = do putStr "Password:"
p <- getLine
if p == "SECRET"
then return ()
else loop1
This is a (tail) recursive function and works just fine.
In a conventional language we might write this as a infinite while loop with a break statement:
def loop():
while True:
p = raw_prompt("Password: ")
if p == "SECRET":
break
With MaybeT we can write the loop in the same manner as the Python code:
loop2 :: IO (Maybe ())
loop2 = runMaybeT $
forever $
do liftIO $ putStr "Password: "
p <- liftIO $ getLine
if p == "SECRET"
then mzero -- break out of the loop
else return ()
The last return () continues execution, and since we are in a forever loop, control passes back to the top of the do block. Note that the only value that loop2 can return is Nothing which corresponds to breaking out of the loop.
Depending on the situation you might find it easier to write loop2 rather than the recursive loop1.
Suppose you have to work with IO values that "may fail" in some sense, like foo :: IO (Maybe a), func1 :: a -> IO (Maybe b) and func2 :: b -> IO (Maybe c).
Manually checking for the presence of errors in a chain of binds quickly produces the dreaded "staircase of doom":
do
ma <- foo
case ma of
Nothing -> return Nothing
Just a -> do
mb <- func1 a
case mb of
Nothing -> return Nothing
Just b -> func2 b
How to "automate" this in some way? Perhaps we could devise a newtype around IO (Maybe a) with a bind function that automatically checks if the first argument is a Nothing inside IO, saving us the trouble of checking it ourselves. Something like
newtype MaybeOverIO a = MaybeOverIO { runMaybeOverIO :: IO (Maybe a) }
With the bind function:
betterBind :: MaybeOverIO a -> (a -> MaybeOverIO b) -> MaybeOverIO b
betterBind mia mf = MaybeOverIO $ do
ma <- runMaybeOverIO mia
case ma of
Nothing -> return Nothing
Just a -> runMaybeOverIO (mf a)
This works! And, looking at it more closely, we realize that we aren't using any particular functions exclusive to the IO monad. Generalizing the newtype a little, we could make this work for any underlying monad!
newtype MaybeOverM m a = MaybeOverM { runMaybeOverM :: m (Maybe a) }
And this is, in essence, how the MaybeT transformer works. I have left out a few details, like how to implement return for the transformer, and how to "lift" IO values into MaybeOverM IO values.
Notice that MaybeOverIO has kind * -> * while MaybeOverM has kind (* -> *) -> * -> * (because its first "type argument" is a monad type constructor, that itself requires a "type argument").
Sure, the MaybeT monad transformer is:
newtype MaybeT m a = MaybeT {unMaybeT :: m (Maybe a)}
We can implement its monad instance as so:
instance (Monad m) => Monad (MaybeT m) where
return a = MaybeT (return (Just a))
(MaybeT mmv) >>= f = MaybeT $ do
mv <- mmv
case mv of
Nothing -> return Nothing
Just a -> unMaybeT (f a)
This will allow us to perform IO with the option of failing gracefully in certain circumstances.
For instance, imagine we had a function like this:
getDatabaseResult :: String -> IO (Maybe String)
We can manipulate the monads independently with the result of that function, but if we compose it as so:
MaybeT . getDatabaseResult :: String -> MaybeT IO String
We can forget about that extra monadic layer, and just treat it as a normal monad.
I'm trying to use the "citation-resolve" package in a Haskell project I'm working on, but I'm having trouble getting my head around using EitherT's in real code. I get that they're monad transformers, and I think I understand what that means, however I can't seem to actually work out how to use them. The toy example that represents what I'm trying to do is as follows:
module Main where
import Text.EditDistance
import Text.CSL.Input.Identifier
import Text.CSL.Reference
import Control.Monad.Trans.Class
import Control.Monad.Trans.Either
main = do
putStrLn "Resolving definition"
let resRef = runEitherT $ resolveEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> do
putStrLn ("Got error: "++ e)
Right ref -> do
putStrLn ("Added reference to database: "++ (show ref))
Here, resolveEither has the type:
resolveEither :: (HasDatabase s,
Control.Monad.IO.Class.MonadIO m,
mtl-2.1.3.1:Control.Monad.State.Class.MonadState s m)
=> String -> EitherT String m Reference
and runEitherT $ resolveEither "ref" has the type:
runEitherT $ resolveEither "ref"
:: (HasDatabase s,
Control.Monad.IO.Class.MonadIO m,
mtl-2.1.3.1:Control.Monad.State.Class.MonadState s m)
=> m (Either String Reference)
However, this gives the following error:
Main.hs:10:34:
No instance for (Control.Monad.IO.Class.MonadIO (Either [Char]))
arising from a use of ‘resolveEither’
In the first argument of ‘runEitherT’, namely
‘(resolveEither "doi:10.1145/2500365.2500595")’
In the expression:
runEitherT (resolveEither "doi:10.1145/2500365.2500595")
In an equation for ‘resRef’:
resRef = runEitherT (resolveEither "doi:10.1145/2500365.2500595")
Which I have no idea how to resolve, or work around.
Any help would be appreciated, especially pointers to tutorials dealing with monad transformers from a usage perspective, not an implementation one.
Edit:
To reflect the comments on answers by dfeuer and Christian, I still get errors if I change main to the following:
main = do
putStrLn "Resolving definition"
resRef <- runEitherT (resolveEither "doi:10.1145/2500365.2500595")
case resRef of
Left e -> do
putStrLn ("Got error: "++ e)
Right ref -> do
putStrLn ("Added reference to database: "++ (show ref))
The error I get now is:
No instance for (MonadState s0 IO)
arising from a use of ‘resolveEither’
In the first argument of ‘runEitherT’, namely
‘(resolveEither "doi:10.1145/2500365.2500595")’
In a stmt of a 'do' block:
resRef <- runEitherT (resolveEither "doi:10.1145/2500365.2500595")
In the expression:
do { putStrLn "Resolving definition";
resRef <- runEitherT (resolveEither "doi:10.1145/2500365.2500595");
case resRef of {
Left e -> do { ... }
Right ref -> do { ... } } }
I'm editing my question as well as commenting, as nice code formatting is substantially easier here than in a comment.
I believe the problem is that you're trying to pattern match on resRef when what you probably want to do is execute it and pattern match on the result.
So you should try this:
main = do
putStrLn "Resolving definition"
resRef <- runEitherT $ resolveEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> do
You've encountered one of the shortcomings of the mtl class-based approach: intimidating type errors. I think it'll be helpful to imagine what the situation would look like with normal transformers-based monad transformers. I hope this will also help you get your feet with monad transformers in general. (It looks like you already understand most of this, by the way; I'm just spelling it out.)
Giving the types is a great way to start. Here's what you had:
resolveEither :: (HasDatabase s,
MonadIO m,
MonadState s m)
=> String -> EitherT String m Reference
There's a type hidden in the constraints, s, which came back to bite you a little later. The constraints, roughly speaking, express the following: s has a database (whatever that means in context); the monad or monad stack m has IO at its base, and somewhere in the monad stack m is a StateT s layer. The simplest monad stack m satisfying those properties would be HasDatabase s => StateT s IO. So we could write this:
resolveEither' :: HasDatabase s
=> String -> EitherT String (StateT s IO) Reference
resolveEither' = resolveEither
All we've done is specify the type of m so it's no longer a variable. We don't need to do that as long as we satisfy the class constraints.
Now it's clearer that there are two layers of monad transformers. Since our main function is in the IO monad, we want to end up with a value of type IO, which we can "run", for instance using <- in do notation. I think of it as "stripping away" layers of the monad transformer, from out to in. (This is what "using" monad transformers boils down to.)
For EitherT, there's a function runEitherT :: EitherT e m a -> m (Either e a). See how the m moves from "inside" the EitherT to "outside"? For me, that's the critical intuitive observation. Similarly for StateT, there's runStateT :: StateT s m a -> s -> m (a, s).
(Incidentally, both are defined as record accessors, which is idiomatic but causes them to show up a bit oddly in Haddock and with the "wrong" type signature; it took me a while to learn to look in the "Constructor" section on Haddocks and mentally add the EitherT e m a -> etc. to the front of the signature.)
So this adds up to a general solution, which you've basically worked out: we need an appropriate value of type s (which I'll call s), then we can use flip runStateT s . runEitherT $ resolveEither "ref" which has type IO ((Either String Reference), s). (Assuming I've kept the types straight in my head, which I probably didn't. I had forgotten flip the first time.) We can then pattern-match or use fst to get to the Either, which seems to be what you really want.
If you'd like me to explicate the errors GHC was giving you, I'd be glad. Informally, it was saying that you weren't "running" or stripping off all the monad transformers. More precisely, it was observing that IO wasn't something like StateT s IO. By using runStateT and runEitherT, you force or constrain the type such that the class constraints end up satisfied. This is kind of confusing when you get things slightly wrong.
Oh, regarding an idiomatic way to write the solution: I'm not sure that a separate retEither function would be idiomatic here, because it looks like it's meddling with global state, i.e. opening some sort of database file. It depends what the library's idiom is like.
Also, by using evalStateT, you're implicitly throwing away the state after evaluation, which may or may not be a bad idea. Does the library expect you to reuse the database connection?
Finally, you have some extra parentheses and some missing type signatures; hlint will help you with those.
Okay, so I think I've worked out a solution to my original problem, which was getting a value of the type IO (Either String Reference) from the function resolveEither (which it does for the resolveDef function it provides).
So, resolveEither returns a type of
(HasDatabase s, MonadIO m, MonadState s m) => String -> EitherT String m Reference
which we can transform to one of type
(HasDatabase s, MonadIO m, MonadState s m) => String -> m (Either String Reference)
using runEitherT . resolveEither. This was where I'd got up to when I asked the question. From there, i tried looking at the source to see how the library extracted a Reference type from the function resolveEither. The library uses the following function:
resolve :: (MonadIO m, MonadState s m, HasDatabase s) => String -> m Reference
resolve = liftM (either (const emptyReference) id) . runEitherT . resolveEither
however, we want to preserve the either, i.e. removing liftM (either (const emptyReference) id)
This however gets us back to where we started, so I looked at the source again, and worked out how this function is used. In the library, the function is used within the following, which transforms the output type of resolve from a value of type (MonadIO m, MonadState s m, HasDatabase s) => m Reference to one of type IO Reference:
resolveDef :: String -> IO Reference
resolveDef url = do
fn <- getDataFileName "default.db"
let go = withDatabaseFile fn $ resolve url
State.evalStateT go (def :: Database)
We can replace resolve in the previous with runEitherT.resolveEither to get a function that returns a IO (Either String Reference):
retEither s = do
fn <- getDataFileName "default.db"
let go = withDatabaseFile fn $ ( (runEitherT.resolveEither) s)
State.evalStateT go (Database Map.empty)
(I've replaced (def :: Database) with (Database Map.empty) as def is only defined internally in citation-resolve)
The overall solution then becomes:
module Main where
import Text.EditDistance
import Text.CSL.Input.Identifier.Internal
import Text.CSL.Input.Identifier
import Text.CSL.Reference
import Control.Monad.Trans.Either
import Control.Monad.State as State
import qualified Data.Map.Strict as Map
main = do
putStrLn "Resolving definition"
resRef <- retEither "doi:10.1145/2500365.2500595"
case resRef of
Left e -> putStrLn ("Got error: "++ e)
Right ref -> putStrLn ("Added reference to database: "++ (show ref))
retEither s = do
fn <- getDataFileName "default.db"
let go = withDatabaseFile fn $ ((runEitherT.resolveEither) s)
State.evalStateT go (Database Map.empty)
Which solves the original problem!
Any pointers on style, or ways of simplifying the whole process would however be very much appreciated.