I have a function "management" that checks parameters and return a Maybe (String):
If there are not parameter -> return Nothing
If my parameter is equal to "-h" -> Return a string help
My problem arrived when I get a file and check if this file exists.
Couldn't match expected type ‘Maybe String’
with actual type ‘IO (Either e0 a2)’
managagment :: [String] -> Maybe (String)
managagment [] = Nothing
managagment ["-h"] = Just (help)
managagment [file] = try $ readFile file >>= \result -> case result of
Left e -> Nothing
Right content -> Just (content)
There are several problems
Function application ($) is lower precedence than bind (>>=)
You said:
try $ readFile file >>= \res...
Which means
try ( readFile file >>= \res... )
But you wanted:
try ( readFile file ) >>= \res...
IO (Maybe a) and Maybe a are distinct
You have a function using IO (via readFile and try) but many of the cases do not return an IO result (Nothing and Just content).
Solution: Return via return Nothing or pure Nothing to lift a value into the IO monad.
The exception type was ambiguous
The try function can catch any exception type, just look at the signature:
try :: Exception e => IO a -> IO (Either e a)
When you totally ignore the exception you leave the type checker with no information to decide what e should be. In these situations an explicit type signature is useful, such as:
Left (e::SomeException) -> pure Nothing
managagment is partial
managagment ["a","b"] is undefined as is any input list of length over one. Consider a final equational definition of:
managagment _ = managagment ["-h"]
Or more directly:
managagment _ = pure $ Just help
Style and other notes
managagment should probably management
Just (foo) is generally Just foo
help is not a function that returns a String. It is a value of type String.
The example was not complete, missing imports and help.
Fixed Code
Consider instead:
#!/usr/bin/env cabal
{- cabal:
build-depends: base
-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
import Control.Exception (try, SomeException)
main :: IO ()
main = print =<< management []
help :: String
help = "so helpful"
management :: [String] -> IO (Maybe String)
management [] = pure Nothing
management ["-h"] = pure $ Just help
management [file] =
try (readFile file) >>=
\case
Left (e::SomeException) -> pure Nothing
Right content -> pure $ Just content
management _ = pure $ Just help
And test as such:
% chmod +x x.hs
% ./x.hs
Related
I am having trouble reading in a level file in Haskell. The goal is to read in a simple txt file with two numbers seperated by a space and then commas. The problem I keep getting is this: Couldn't match type `IO' with `[]'
If I understand correctly the do statement is supposed to pull the String out of the Monad.
readLevelFile :: FilePath -> [FallingRegion]
readLevelFile f = do
fileContent <- readFile f
(map lineToFallingRegion (lines fileContent))
lineToFallingRegion :: String -> FallingRegion
lineToFallingRegion s = map textShapeToFallingShape (splitOn' (==',') s)
textShapeToFallingShape :: String -> FallingShape
textShapeToFallingShape s = FallingShape (read $ head numbers) (read $ head
$ tail numbers)
where numbers = splitOn' (==' ') s
You can't pull things out of IO. You can think of IO as a container (in fact, some interpretations of IO liken it to the box containing Schrödinger's cat). You can't see what's in the container, but if you step into the container, values become visible.
So this should work:
readLevelFile f = do
fileContent <- readFile f
return (map lineToFallingRegion (lines fileContent))
It does not, however, have the type given in the OP. Inside the do block, fileContent is a String value, but the entire block is still inside the IO container.
This means that the return type of the function isn't [FallingRegion], but IO [FallingRegion]. So if you change the type annotation for readLevelFile to
readLevelFile :: FilePath -> IO [FallingRegion]
you should be able to get past the first hurdle.
Let's look at your first function with explicit types:
readLevelFile f = do
(fileContent :: String) <-
(readFile :: String -> IO String) (f :: String) :: IO String
fileContent is indeed of type String but is only available within the execution of the IO Monad under which we are evaluating. Now what?
(map lineToFallingRegion (lines fileContent)) :: [String]
Now you are suddenly using an expression that is not an IO monad but instead is a list value - since lists are also a type of monad the type check tries to unify IO with []. What you actually wanted is to return this value:
return (map lineToFallingRegion (lines fileContent)) :: IO [String]
Now recalling that we can't ever "exit" the IO monad your readLevelFile type must be IO - an honest admission that it interacts with the outside world:
readLevelFile :: FilePath -> IO [FallingRegion]
According to https://hackage.haskell.org/package/ConfigFile-1.0.5/docs/Data-ConfigFile.html, the package will convert a value in a config. file to a Bool. The following code:
{-# LANGUAGE FlexibleContexts #-}
import qualified Data.ConfigFile as DC
import qualified Control.Monad.Except as CME
-- | The foundation object
data JRState = JRState {
secureOnly :: Bool -- ^ restrict connections to HTTPS
}
main :: IO ()
main = (CME.runExceptT $ pipe (JRState False)) >>= estate
estate :: Show t => Either t JRState -> IO ()
estate (Right state) = return ()
estate (Left err) = do
putStrLn $ "<<" ++ show err ++ ">>"
return ()
pipe :: (CME.MonadError DC.CPError m, CME.MonadIO m) => JRState -> m JRState
pipe site = do
cp <- CME.join $ CME.liftIO $ return $ DC.readstring DC.emptyCP{DC.optionxform=id} "secureSession = True\n"
DC.get cp "DEFAULT" "secureSession" >>= return . nubb where
nubb (Left err) = error err
nubb (Right value) = site{secureOnly = value}
when run, produces
<<(ParseError "couldn't parse value True from (DEFAULT/secureSession)","genericget")>>
which has obviously come from the putStrLn in estate. But I would expect that the extraction of the value, in pipe and nubb (silly names, I know) would force a Boolean context and thus force the conversion of the True string to a Bool. I've tried 1 and Yes with the same result. What's going on?
Here is a more minimal program with similarly problematic behavior:
import qualified Data.ConfigFile as DC
import qualified Control.Monad.Except as CME
main = CME.runExceptT pipe >>= print
pipe = do
cp <- DC.readstring DC.emptyCP{DC.optionxform=id} "secureSession = True\n"
DC.get cp "DEFAULT" "secureSession" >>= nubb
nubb :: Either String Bool -> m Bool
nubb = undefined
When it's stripped down to this bare-bones form, it's obvious what has gone wrong: you are asking DC.get to return an Either String Bool when in fact you should simply be asking it to return a Bool. Simple fix for the stripped-down version is to eliminate the >>= nubb part of that line entirely; it should be easy to translate this fix back into your bigger context.
It is quite hard to formulate good questions titles as a newbie. Please make this question search friendly =)
Trying to write my first "real" Haskell program (i.e. not only Project Euler stuff), I am trying to read and parse my configuration file with nice error messages. So far, I have this:
import Prelude hiding (readFile)
import System.FilePath (FilePath)
import System.Directory (doesFileExist)
import Data.Aeson
import Control.Monad.Except
import Data.ByteString.Lazy (ByteString, readFile)
-- Type definitions without real educational value here
loadConfiguration :: FilePath -> ExceptT String IO Configuration
loadConfiguration path = do
fileContent <- readConfigurationFile "C:\\Temp\\config.json"
configuration <- parseConfiguration fileContent
return configuration
readConfigurationFile :: FilePath -> ExceptT String IO ByteString
readConfigurationFile path = do
fileExists <- liftIO $ doesFileExist path
if fileExists then do
fileContent <- liftIO $ readFile path
return fileContent
else
throwError $ "Configuration file not found at " ++ path ++ "."
parseConfiguration :: ByteString -> ExceptT String IO Configuration
parseConfiguration raw = do
let result = eitherDecode raw :: Either String Configuration
case result of
Left message -> throwError $ "Error parsing configuration file: " ++ message
Right configuration -> return configuration
This works, but the IO monad in parseConfiguration is not necessary, and should go away. But I can't just drop it, of course, and I have not yet found a way to change parseConfiguration to something pure while keeping the prettyness of loadConfiguration.
What is the correct way to write this? If this is answered in the documentation, I am sorry, but I did not find it. I think reading the hackage documentation is a skill that grows as slowly as the rest of my Haskell skills. =)
P.S.: Comments on other style mistakes are, of course, very welcome!
If you are already using mtl, then the solution given by bheklilr in his comment is a good one. Make parseConfiguration work on any monad that implements MonadError.
If for whatever reason you are not using mtl, but only transformers, then you need'll a function with a type like Monad n => Except e a -> ExceptT e n a that "hoists" an Except into an ExceptT over some monad.
We can build this function using mapExceptT :: (m (Either e a) -> n (Either e' b)) -> ExceptT e m a -> ExceptT e' n b, a function that can change the base monad of an ExceptT transformer.
Except is really ExceptT Identity, so what we want is to unwrap the Identity and return the value in the new monad:
hoistExcept :: Monad n => Except e a -> ExceptT e n a
hoistExcept = mapExceptT (return . runIdentity)
You could also define it this way:
hoistExcept :: Monad n => Except e a -> ExceptT e n a
hoistExcept = ExceptT . return . runIdentity . runExceptT
I am trying to understand Monads in Haskell and during my countless experiments with code I have encountered this thing:
f2 = do
return "da"
and the fact that it doesnt want to compile with huge error regarding type. I think the only important part is this:
No instance for (Monad m0) arising from a use of return'
The type variable `m0' is ambiguous
So then I have changed my code to:
f2 = do
return "da" :: IO [Char]
And it worked perfectly well. But when I have tried to mess up a bit and change the type to IO Int it was an error once again. So why the type is "ambiguous" when it actually isnt?
Also when I will add something before return like:
f2 = do
putStrLn "das"
return 2
Then I dont have to specify the type of return.
So can someone explain me what is going on really? Also why is return outputting "da" in the first case? Not da without ""?
First let's just point out that
do
return a
Is exactly the same as
return a
Now, the problem is that return has the type
return :: Monad m => a -> m a
And when you have a declaration like
foo = bar
where foo has no arguments haskell makes it "monomorphic". The result of this is that Haskell can't guess what m is and won't generalize it so you need an explicit type signature. The most general one is
f2 :: Monad m => m String
f2 = return "das"
But you could also use IO or any other monad
f2 :: IO String
Finally, in your last example, since you're returning 2, you'd have to give a type signature that indicates you're returning some sort of number, like
f2 :: IO Integer
This is known Monomorphism_restriction
Use signatures
f2 :: Monad m => m String
f2 = do
return "da"
or use language extension:
{-# LANGUAGE NoMonomorphismRestriction #-}
f2 = do
return "da"
to get valid code
When learning about monads it's helpful to expand them out manually yourself, for instance the simple example:
test0 :: IO String
test0 = do
a <- getLine
putStrLn a
return a
If we enable the language extension {-# LANGUAGE ScopedTypeVariables #-} then we can annotate each of the lines in the monad with it's explicit type which will show the type of the return block.
{-# LANGUAGE ScopedTypeVariables #-}
test1 :: IO String
test1 = do
a <- getLine :: IO String
putStrLn a :: IO ()
return a :: IO String
We can also annotate the explicit type of the left hand side pattern matching which "extracts" from the monad context on the right hand side.
test2 :: IO String
test2 = do
(a :: String) <- getLine :: IO String
(() :: ()) <- putStrLn a :: IO ()
return a :: IO String
We can even expand out the do-notation into its constituting parts:
test3 :: IO String
test3 = getLine >>=
(\a -> putStrLn a >>=
\() -> return a)
Hope that helps build your monad intuition.
I am building a Haskell application and trying to figure out how I am going to build the error handling mechanism. In the real application, I'm doing a bunch of work with Mongo. But, for this, I'm going to simplify by working with basic IO operations on a file.
So, for this test application, I want to read in a file and verify that it contains a proper fibonnacci sequence, with each value separated by a space:
1 1 2 3 5 8 13 21
Now, when reading the file, any number of things could actually be wrong, and I am going to call all of those exceptions in the Haskell usage of the word.
data FibException = FileUnreadable IOError
| FormatError String String
| InvalidValue Integer
| Unknown String
instance Error FibException where
noMsg = Unknown "No error message"
strMsg = Unknown
Writing a pure function that verifies the sequence and throws an error in the case that the sequence is invalid is easy (though I could probably do better):
verifySequence :: String -> (Integer, Integer) -> Either FibException ()
verifySequence "" (prev1, prev2) = return ()
verifySequence s (prev1, prev2) =
let readInt = reads :: ReadS Integer
res = readInt s in
case res of
[] -> throwError $ FormatError s
(val, rest):[] -> case (prev1, prev2, val) of
(0, 0, 1) -> verifySequence rest (0, 1)
(p1, p2, val') -> (if p1 + p2 /= val'
then throwError $ InvalidValue val'
else verifySequence rest (p2, val))
_ -> throwError $ InvalidValue val
After that, I want the function that reads the file and verifies the sequence:
type FibIOMonad = ErrorT FibException IO
verifyFibFile :: FilePath -> FibIOMonad ()
verifyFibFile path = do
sequenceStr <- liftIO $ readFile path
case (verifySequence sequenceStr (0, 0)) of
Right res -> return res
Left err -> throwError err
This function does exactly what I want if the file is in the invalid format (it returns Left (FormatError "something")) or if the file has a number out of sequence (Left (InvalidValue 15)). But it throws an error if the file specified does not exist.
How do I catch the IO errors that readFile may produce so that I can transform them into the FileUnreadable error?
As a side question, is this even the best way to do it? I see the advantage that the caller of verifyFibFile does not have to set up two different exception handling mechanisms and can instead catch just one exception type.
You might consider EitherT and the errors package in general. http://hackage.haskell.org/packages/archive/errors/1.3.1/doc/html/Control-Error-Util.html has a utility tryIO for catching IOError in EitherT and you could use fmapLT to map error values to your custom type.
Specifically:
type FibIOMonad = EitherT FibException IO
verifyFibFile :: FilePath -> FibIOMonad ()
verifyFibFile path = do
sequenceStr <- fmapLT FileUnreadable (tryIO $ readFile path)
hoistEither $ verifySequence sequenceStr (0, 0)
#Savanni D'Gerinel: you are on the right track. Let's extract your error-catching code from verifyFibFile to make it more generic, and modify it slightly so that it works directly in ErrorT:
catchError' :: ErrorT e IO a -> (IOError -> ErrorT e IO a) -> ErrorT e IO a
catchError' m f =
ErrorT $ catchError (runErrorT m) (fmap runErrorT f)
verifyFibFile can now be written as:
verifyFibFile' :: FilePath -> FibIOMonad ()
verifyFibFile' path = do
sequenceStr <- catchError' (liftIO $ readFile path) (throwError . FileUnReadable)
ErrorT . return $ verifySequence sequenceStr' (0, 0)
Notice what we have done in catchError'. We have stripped the ErrorT constructor from the ErrorT e IO a action, and also from the return value of the error-handling function, knowing than we can reconstruct them afterwards by wrapping the result of the control operation in ErrorT again.
Turns out that this is a common pattern, and it can be done with monad transformers other than ErrorT. It can get tricky though (how to do this with ReaderT for example?). Luckily, the monad-control packgage already provides this functionality for many common transformers.
The type signatures in monad-control can seem scary at first. Start by looking at just one function: control. It has the type:
control :: MonadBaseControl b m => (RunInBase m b -> b (StM m a)) -> m a
Let's make it more specific by making b be IO:
control :: MonadBaseControl IO m => (RunInBase m IO -> IO (StM m a)) -> m a
m is a monad stack built on top of IO. In your case, it would be ErrorT IO.
RunInBase m IO is a type alias for a magical function, that takes a value of type m a and returns a value of type IO *something*, something being some complex magic that encodes the state of the whole monad stack inside IO and lets you reconstruct the m a value afterwards, once you have "fooled" the control operation that only accepts IO values. control provides you with that function, and also handles the reconstruction for you.
Applying this to your problem, we rewrite verifyFibFile once more as:
import Control.Monad.Trans.Control (control)
import Control.Exception (catch)
verifyFibFile'' :: FilePath -> FibIOMonad ()
verifyFibFile'' path = do
sequenceStr <- control $ \run -> catch (run . liftIO $ readFile path)
(run . throwError . FileUnreadable)
ErrorT . return $ verifySequence sequenceStr' (0, 0)
Keep in mind that this only works when the proper instance of MonadBaseControl b m exists.
Here is a nice introduction to monad-control.
So, here's an answer that I have developed. It centers around getting readFile wrapped into the proper catchError statement, and then lifted.
verifyFibFile :: FilePath -> FibIOMonad ()
verifyFibFile path = do
contents <- liftIO $ catchError (readFile path >>= return . Right) (return . Left . FileUnreadable)
case contents of
Right sequenceStr' -> case (verifySequence sequenceStr' (0, 0)) of
Right res -> return res
Left err -> throwError err
Left err -> throwError err
So, verifyFibFile gets a little more nested in this solution.
readFile path has type IO String, obviously. In this context, the type for catchError will be:
catchError :: IO String -> (IOError -> IO String) -> IO String
So, my strategy was to catch the error and turn it into the left side of an Either, and turn the successful value into the right side, changing my data type to this:
catchError :: IO (Either FibException String) -> (IOError -> IO (Either FibException String)) -> IO (Either FibException String)
I do this by, in the first parameter, simply wrapping the result into Right. I figure that I won't actually execute the return . Right branch of the code unless readFile path was successful. In the other parameter to catch, I start with an IOError, wrap it in Left, and then return it back into the IO context. After that, no matter what the result is, I lift the IO value up into the FibIOMonad context.
I'm bothered by the fact that the code gets even more nested. I have Left values, and all of those Left values get thrown. I'm basically in an Either context, and I had thought that one of the benefits Either's implementation of the Monad class was that Left values would simply be passed along through the binding operations and that no further code in that context would be executed. I would love some elucidation on this, or to see how the nesting can be removed from this function.
Maybe it can't. It does seem that the caller, however, can call verifyFibFile repeatedly and execution basically stops the first time verifyFibFile returns an error. This works:
runTest = do
res <- verifyFibFile "goodfib.txt"
liftIO $ putStrLn "goodfib.txt"
--liftIO $ printResult "goodfib.txt" res
res <- verifyFibFile "invalidValue.txt"
liftIO $ putStrLn "invalidValue.txt"
res <- verifyFibFile "formatError.txt"
liftIO $ putStrLn "formatError.txt"
Main> runErrorT $ runTest
goodfib.txt
Left (InvalidValue 17)
Given the files that I have created, both invalidValue.txt and formatError.txt cause errors, but this function returns Left (InvalidValue ...) for me.
That's okay, but I still feel like I've missed something with my solution. And I have no idea whether I'll be able to translate this into something that makes MongoDB access more robust.