Handle nested Maybes [Haskell] - haskell

I have a function that requires the handling of two Maybes. This is an extremely naive implementation:
go :: IO (Maybe Int)
go = do
res1 <- firstFunction
case res1 of
Nothing -> return Nothing
Just a -> do
res2 <- secondFunction a
case res2 of
Nothing -> return Nothing
Just b -> return b
How could I structure this so that I can use the result a of the firstFunction in my secondFunction?

Use MaybeT IO a instead of IO (Maybe a). Then:
firstFunction :: MaybeT IO X
firstFunction = undefined -- ...
secondFunction :: X -> MaybeT IO Int
secondFunction = undefined -- ...
go :: IO (Maybe Int)
go = do
a <- firstFunction
secondFunction a
-- OR
go = firstFunction >>= secondFunction
You may use these two halves of an isomorphism to convert between the two types:
MaybeT :: IO (Maybe a) -> MaybeT IO a
runMaybeT :: MaybeT IO a -> IO (Maybe a)
To reduce the total number of uses of them, I recommend pushing the former as deep into the leaves of your computation as you can, and lifting the latter as far out as you can. You may also like these actions for defining leaf computations:
lift :: IO a -> MaybeT IO a
liftIO :: IO a -> MaybeT IO a
empty :: MaybeT IO a -- for when you want a Nothing
pure :: a -> MaybeT IO a -- for when you want a Just
maybe empty pure :: Maybe a -> MaybeT IO a

Related

Different type of function and `in do statement`

main :: IO ()
main = do
args <- getArgs
case args of
[] -> putStrLn "Give an argument!"
It works, but why? Returned type is IO (). getArgs returned IO [String] ( not IO ()). So it should raise an error.
type C = ErrorT String IO
main :: C ()
main = do
args <- getArgs
case args of
[] -> putStrLn "Give an argument!"
putStrLn tries to return IO (). But it isn't compatible with C. So, should I liftIO? I see that I should. Could you show me how does liftIO works here? Why I have to use liftIO with getArgs?
To figure out what is going on, you can try de-sugaring you function:
main :: IO ()
main = getArgs >>= \ args ->
case args of
[] -> putStrLn "Give an argument!"
Here, getArgs has type IO [String] and the function (let's call it f) has type [String] -> IO (). Now lets see what's going on with (>>=):
(>>=) :: m a -> (a -> m b) -> m b
-- a unifies with [String] and m with IO
(getArgs >>=) :: ([String] -> IO b) -> IO b
(>>=) :: IO [String] -> ([String] -> IO b) -> IO b
-- b unifies with ()
getArgs >>= f :: IO ()
(>>=) :: IO [String] -> ([String] -> IO ()) -> IO ()
For your example with C, there are three things wrong:
GHC expects main to have type IO (), but it has type C () in your snippet
getArgs is expected to have type C [String], but it has type IO [String]
same for putStrLn, which must have type C ()
To fix this, since ErrorT is a monad transformer, you can lift a monadic action to the level of the transformer with lift.
liftIO is a lift operation that can be applied only with monad transforming IO. The interesting thing is that it can work with any monad transformer (with as much monad transformer depth as you want): With liftIO, you can work with T1 (T2 (... (Tn IO)...)) a for any n where Tn are monad transformers.
You can notice their kind of similar signatures:
lift :: (MonadTrans t, Monad m) => m a -> t m a
liftIO :: MonadIO m => IO a -> m a
You don't need to specify IO for m, in the signature of liftIO, because it's instance of MonadIO already tells us that it can perform only IO actions.
To conclude, this is how you would use liftIO in your program:
type C = ErrorT String IO
main :: IO ()
main = runErrorT go >>= print -- print the result of type `Either String ()`
where go :: C ()
go = do
args <- liftIO getArgs
case args of
[] -> liftIO $ putStrLn "Give an argument!"
But, here, since you don't use any features of the ErrorT monad, the code is not really a good example.
Also, note that if you are talking about ErrorT in the mtl package, it is deprecated, consider using ExceptT.
Check out this post for more information about lift vs liftIO
If you desugar the do notation:
main = getArgs >>= (\ args -> case args of [] -> putStrLn "Argh!")
getArgs has type IO [String]. >>= pipes the resulting [String] into the lambda, which returns IO (), the result of putStrLn "...". Here, >>= has the inferred type:
(>>=) :: IO [String] -> ([String] -> IO ()) -> IO ()
So the type of the whole expression is IO (), matching the signature of main.

How can I extract an IO () from Maybe and execute this?

I have this code:
trick = Just (putStrLn "Hello?")
And I want to unwrap this IO () out the Maybe context and call it.
main = do foo <- trick
foo
However, this throws an error:
Couldn't match type ‘IO’ with ‘Maybe’
Expected type: Maybe ()
Actual type: IO ()
How could I resolve this?
The function you are looking for is Data.Foldable.sequence_:
>>> Data.Foldable.sequence_ (Just (putStrLn "Hello?"))
Hello?
>>>
If your Maybe is Nothing, it will not do anything:
>>> Data.Foldable.sequence_ Nothing
>>>
This works because the type of Data.Foldable.sequence_ is:
Data.Foldable.sequence_
:: (Foldable t, Monad m) => t (m a) -> m ()
... and if you specialize t to Maybe and m to IO, you get:
Data.Foldable.sequence_ :: Maybe (IO a) -> IO ()
In the specific context of Maybe, it's equivalent to:
sequence_ (Just io) = do
_ <- io
return ()
sequence Nothing = return ()
The easiest solution to start out with is probably to realize that you can decide what to do based on the value of the Maybe (IO ()) via pattern matching.
maybeDoIO :: Maybe (IO ()) -> IO ()
maybeDoIO (Just io) = io
maybeDoIO Nothing = return ()
You have to take the Nothing into account, basically:
main = maybe (putStrLn "Nothing") id foo
The problem with your main function is that you are mixing two different monads in the same do block.
The foo <- trick action is "relative" to the Maybe monad but the foo action afterwards is an IO action. The type for >>= is:
Monad m => m a -> (a -> m b) -> m b
But in your case you'd need something with type:
Maybe a -> (a -> IO b) -> IO b
with two different monads.
If you want to execute the IO action then the type of main must be of the kind IO a so the do notation must refer to IO and not Maybe. Which means that you cannot use <- to extract the action but you have to use something else. For example Data.Maybe.fromMaybe:
import Data.Maybe
main = do fromMaybe (return ()) trick

Convert from IO Maybe a to EitherT IO a

I'm working with the servant library for an API. It runs in the: EitherT (Int, String) IO a monad. I have a function with the type IO Maybe a, and would like to use it.
Here's an example that works:
sourcesGetAll :: EitherT (Int, String) IO [Source]
sourcesGetAll = liftIO $ sourcesList h
sourcesList :: IO [Source]
But now I want to use these two functions together
sourcesFind :: IO (Maybe Source)
sourcesGetOne :: EitherT (Int, String) IO Source
sourcesGetOne = ???
I would like to do this:
maybeNotFound :: Maybe a -> Either (Int, String) a
maybeNotFound Nothing = Left (404, "Not Found")
maybeNotFound Just a = Right a
How do I do that with all the fancy monads?
You can use hoistEither :: Monad m => Either a b -> EitherT a m b to implement this:
maybeNotFoundT :: IO (Maybe a) -> EitherT (Int, String) IO a
maybeNotFoundT maAct = do
ma <- liftIO maAct -- get the Maybe a. Now ma :: Maybe a
hoistEither $ maybeNotFound ma
You can decompose this into two separate problems:
Transform IO (Maybe a) into MaybeT IO a
Transform MaybeT IO a into EitherT (Int, String) a
The first one is solved by using the MaybeT constructor:
MaybeT :: IO (Maybe a) -> MaybeT IO a
The second is solved by using noteT from the errors library:
noteT :: Monad m => a -> MaybeT m b -> EitherT a m b
Another answer thanks to mpickering on freenode:
sourcesGetOne = EitherT $ maybeNotFound <$> sourcesFind
So the question is how to write a function.. IO (Maybe a) -> EitherT (Int, String) IO a
Given a function f :: Maybe a -> Either (Int, String) a, then one way is..
myFunc action = EitherT (f <$> action)
If you look at the docs for EitherT -- https://hackage.haskell.org/package/either-4.3.3.2/docs/Control-Monad-Trans-Either.html. Then you'll see that EitherT (Int, String) IO a, is actually just a wrapped up IO (Either (Int, String) a)

Types in MaybeT computation

Working in an IO computation I ended up with a staircase of case mbValue of …s and figured out that I should use the Maybe monad to simplify the code. Since it's within an IO computation and I need to get IO values, I used the MaybeT monad transformer so that I can lift IO computation into Maybe.
Now, I have always thought about values being “stripped” of their Maybeness after an values <- mbValue inside a Maybe computation, but this turns out to be too simple of a heuristic here.
As highlighted below, when using a Maybe a value as an a (here by passing it to read), it fails to type check:
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT)
lol :: IO (Maybe Int)
lol = return (Just 3)
lal :: IO (Maybe String)
lal = return (Just "8")
foo :: IO (Maybe Bool)
foo = do
b <- runMaybeT $ do
x <- lift lol
y <- lift lal
return (x < (read y))
return b ^-- Couldn't match type ‘Maybe String’ with ‘String’
main = foo >>= print
If I put a typed hole in for return (x < (read y)), I see that it expects a Bool, which makes sense, but also that the current bindings include
|| y :: Data.Maybe.Maybe GHC.Base.String
|| (bound at /private/tmp/test.hs:14:5)
|| x :: Data.Maybe.Maybe GHC.Types.Int
|| (bound at /private/tmp/test.hs:13:5)
I.e., y is a Maybe String. This of course explains the error, but I'm left confused. Where is my understanding wrong, and how can I fix this error?
In short: Replace lift by the MaybeT constructor.
Note that
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
and
lift :: (MonadTrans t, Monad m) => m a -> t m a
Your use of lift in
x <- lift lol
is at the type
lift :: IO (Maybe Int) -> MaybeT IO (Maybe Int)
That's why x will be a Maybe Int again. The lift adds a fresh MaybeT layer that is independent of the Maybe occurrence you already have.
But
MaybeT :: m (Maybe a) -> MaybeT m a
instead as in
x <- MaybeT lol
will be used at type
MaybeT :: IO (Maybe a) -> MaybeT IO a
and do the right thing.
When specialized to MaybeT, lift :: Monad m => m a -> MaybeT m a. Since lol :: IO (Maybe Int), m is IO and a is Maybe Int, therefore lift lol :: MaybeT IO (Maybe Int).
IO (Maybe a) is just the value contained within a MaybeT IO a newtype wrapper, so there's no need to lift it; instead use the MaybeT constructor, for example as in MaybeT lol.
But this is not how people tend to use monad transformers. Instead, just use MaybeT values and lift as needed:
import Control.Monad
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT, MaybeT)
lol :: MaybeT IO Int
lol = return 3
lal :: MaybeT IO String
lal = return "8"
foo :: IO (Maybe Bool)
foo =
runMaybeT $ do
x <- lol
y <- lal
_ <- lift getLine -- lift (IO String) to MaybeT IO String
_ <- return 100 -- lift any pure value
_ <- mzero -- use the MonadPlus instance to get a lifted Nothing.
return (x < (read y))
main = foo >>= print

Working with Maybe a, IO a, and MaybeT IO a

I'm writing a prompt - response style system with a bunch of various combinations of Maybe a, IO a, and MaybeT IO a, and there is a lof of stuff to take into account. Some IO actions for which there is no invalid input (and therefore aren't wrapped in MaybeT), some which are (and return an MaybeT IO a) some which aren't IO actions but can fail, so return Maybe a, and some that are just plain values and its beginning to seem that I have to remember inordinate combinations of <$>, Just, fmap, MaybeT, lift, =<<, and return just to get everything to be the right type. Is there any easier way to manage this or to reason about what functions I need to use to get my values where I need them? Or do I just have to hope I get better at it with time? Here is my example:
getPiece :: Player -> Board -> MaybeT IO Piece
getPiece player#(Player pieces _ _ _) board = piece
where
promptString = displayToUserForPlayer player board ++ "\n" ++ (display player) ++ "\n" ++ "Enter piece number: "
input :: MaybeT IO String
input = lift $ prompt promptString
index :: MaybeT IO Int
index = MaybeT <$> return <$> ((fmap cvtFrom1indexedInt) . maybeRead) =<< input
piece :: MaybeT IO Piece
piece = MaybeT <$> return <$> maybeIndex pieces =<< index
getRotatedPiece :: Player -> Board -> MaybeT IO Piece
getRotatedPiece player#(Player pieces _ _ _) board = piece
where
promptString :: MaybeT IO String
promptString = (++) <$> displayListString <*> restOfString
input :: MaybeT IO String
input = MaybeT <$> (fmap Just) <$> prompt =<< promptString
index :: MaybeT IO Int
index = MaybeT <$> return <$> ((fmap cvtFrom1indexedInt) . maybeRead) =<< input
piece :: MaybeT IO Piece
piece = MaybeT <$> return <$> maybeIndex pieces =<< index
rotatedPieceList :: MaybeT IO [Piece]
rotatedPieceList = rotations <$> getPiece player board
displayListString :: MaybeT IO String
displayListString = displayNumberedList <$> rotatedPieceList
restOfString :: MaybeT IO String
restOfString = MaybeT <$> return <$> Just $ "\nEnter rotation number:"
I must say, I am disappointed at the lack of conciseness, even if I removed the type hints I could likely write a shorter function to do the same thing in C# or python
Since you provided only a code fragment, I cannot try to refactor it. However, this is what I'd do: Most monads have a corresponding type class. The reason for it is exactly what you need here: When you create a monad using a monad transformer, it will inherit the operations of the inner monads (if appropriate). So you can forget about the inner monads and work just within the final monad.
In your case, you have MaybeT IO. It's instance of MonadPlus and of MonadIO. So you can refactor the code that returns Maybe something to work with a general MonadPlus instance instead, just replace Just with return and Nothing with mzero. Like:
-- before
checkNumber :: Int -> Maybe Int
checkNumber x | x > 0 = Just x
| otherwise = Nothing x
-- after
checkNumber :: MonadPlus m => Int -> m Int
checkNumber x | x > 0 = return x
| otherwise = mzero
-- or just: checkNumber = mfilter (> 0) . return
It will work with any MonadPlus, including Maybe and MaybeT IO.
And you can refactor the code that returns IO something to work with a general MonadIO instance:
-- before
doSomeIO :: IO ()
doSomeIO = getLine >>= putStrLn
-- after
doSomeIO :: MonadIO m => m ()
doSomeIO = liftIO $ getLine >>= putStrLn
This way, you can forget about <$>/fmap/liftM, Just, MaybeT etc. You just use return, mzero and in some places liftIO.
This will also help you to create a more general code. If you later realize that you need to add something to the monad stack, the existing code won't break, as long as the new monad stack implements the same type classes.
A less ambitious answer from me. Looking at your code, your operations like getPiece don't really return any information from the a particular error site. You can probably get away with just using IO and turning exceptions into Maybe values if you really want those. Some sample code I put together with some undefined functions referenced in your code:
import Control.Exception (handle, IOException)
data Board = Board deriving (Show)
data Piece = Piece deriving (Show)
type Pieces = [Piece]
data Player = Player Pieces () () () deriving (Show)
prompt :: String -> IO String
prompt = undefined
cvtFrom1indexedInt :: Int -> Int
cvtFrom1indexedInt = undefined
maybeIndex :: Pieces -> Int -> Maybe Piece
maybeIndex = undefined
displayToUserForPlayer :: Player -> Board -> String
displayToUserForPlayer = undefined
display :: Player -> String
display = undefined
-- I used this when testing, to deal with the Prelude.undefined errors
--returnSilently :: SomeException -> IO (Maybe a)
returnSilently :: IOException -> IO (Maybe a)
returnSilently e = return Nothing
getPiece :: Player -> Board -> IO (Maybe Piece)
getPiece player#(Player pieces _ _ _) board = handle returnSilently $ do
let promptString = displayToUserForPlayer player board ++ "\n" ++ (display player) ++ "\n" ++ "Enter piece number: "
input <- prompt promptString
let index = cvtFrom1indexedInt (read input)
return (maybeIndex pieces index)
main = do
maybePiece <- getPiece (Player [] () () ()) Board
putStrLn ("Got piece: " ++ show maybePiece)
Notably I've moved from MaybeT IO Piece to just IO (Maybe Piece). Instead of using fmap or lift I've just used do notation for referring to the intermediate results of my IO action.
Going on your comments about C# or Python, I hope this was the sort of simpler answer you were looking for.

Resources