I found out that words is the appropriate function to split a String:
words :: String -> [String]
What's special about fmap that makes this code work:
Prelude> fmap words getLine
abc def ghi
["abc","def","ghi"]
and it's omission results in a error:
Prelude> words getLine
<interactive>:10:7: error:
• Couldn't match type ‘IO String’ with ‘[Char]’
Expected type: String
Actual type: IO String
• In the first argument of ‘words’, namely ‘getLine’
In the expression: words getLine
In an equation for ‘it’: it = words getLine
I know that fmap works with Functors but I don't know about Functors yet. Is it related to that?
getLine returns an IO String, so words getLine is a type error. fmap has type
Functor f => (a -> b) -> f a -> f b
and IO has a functor instance so fmap for IO has type (a -> b) -> IO a -> IO b. fmap words therefore has type IO String -> IO [String], and applying this to the result of getLine results in an IO [String].
In ghci, IO actions are executed an their results printed, which is why you see the result list displayed.
Related
I'm trying to extract "a" and the error "b" from an expression of the type IO (Either a b).
I have this function, which returns a parsed file based on the file path
readFile' :: FilePath -> IO (Either a b)
And that's the way I'm trying to extract the values of a and b:
rFile :: FilePath -> String
rFile f = do
l <- readFile' f
case l of
Right n -> show n
Left m -> show m
This is the error message:
Couldn't match type `IO' with `[]'
Expected type: [Either a b]
Actual type: IO (Either a b)
* In a stmt of a 'do' block: l <- readFile' f
rFile can't return a String value, only an IO String value. (Or more precisely, the result of the do construct must be an IO String value, not a String.)
rFile :: FilePath -> IO String
rFile f = do
l <- readFile' f
return $ case l of
Right n -> show n
Left m -> show m
You can use fmap and either to get rid of both the explicit case analysis and the do syntax.
rFile f = fmap (either show show) (readFile' f)
(The obvious follow-up question, how do I get a String from an IO String, is one I'm not going to rehash here. For all practical intents and purposes, you don't.)
The lines function in Haskell separates the lines of a string into a string list:
lines :: String -> [String]
The readFile function reads a file into a string:
readFile :: FilePath -> IO String
Trying to compose these functions to get a list of lines in a file results in a type error:
Prelude> (lines . readFile) "quux.txt"
<interactive>:26:10: error:
• Couldn't match type ‘IO String’ with ‘[Char]’
Expected type: FilePath -> String
Actual type: FilePath -> IO String
• In the second argument of ‘(.)’, namely ‘readFile’
In the expression: lines . readFile
In the expression: (lines . readFile) "quux.txt"
How can I do the monad trick here?
You can't compose them, at least not with (.) alone. You can use fmap (or its operator version <$>), though:
lines <$> readFile "quux.txt" -- Produces IO [String], not [String]
One way to express this in terms of a kind of composition is to first create a Kleisli arrow (a function of type a -> m b for some monad m) from lines:
-- return . lines itself has type Monad m => String -> m [String]
-- but for our use case we can restrict the type to the monad
-- we are actually interested in.
kleisliLines :: String -> IO [String]
kleisliLines = return . lines
Now you can use the Kleisli composition operator >=> to combine readFile (itself a Kleisli arrow) and lines:
import Control.Monad -- where (>=>) is defined
-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
-- Here, m ~ IO
-- a -> FilePath
-- b -> String
-- c -> [String]
(readFile >=> kleisliLines) "quux.txt"
Compare this with the >>= operator, which requires you to supply the file name to readFile before feeding the result to return . lines:
-- m >>= return . f === fmap f m === f <$> m
readFile "quux.txt" >>= kleisliLines
>=> is natural if you are already thinking of a pipeline in terms of >=; if you want something that preserves the order of ., use <=< (also defined in Control.Monad, as (<=<) = flip (>=>); the operands are simply reversed).
(kleisliLines <=< readFile) "quux.txt"
The other answers given so far have been to make lines produce an empty monadic context, and then use monadic composition (<=<) to compose it with readFile. But you can also go the other direction: lift lines to operate through a monadic argument, and then use ordinary composition to combine it with readFile:
(fmap lines . readFile) "quux.txt"
Of course, if you're going to immediately apply this to an argument, it's simpler to just write
lines <$> readFile "quux.txt"
Can someone explain to me why when using ghci I have to "re-box" the value returned by a monad?
listAction=listDirectory "D:\\"
lenAction=listAction>>=length
Error:
Couldn't match type `Int' with `IO b0'
Expected type: [FilePath] -> IO b0
Actual type: [FilePath] -> Int
I do not understand where my logic is flawed:
Using listAction gives me a IO [FilePath]
I unbox the value of listAction to [FilePath] and i give it to length
Now shouldn't length just print the result to the console being a pure function? Isn't it the same if i would say length [1,2,3]?
Why do i have to box it again? Isn't the result of the monad of type: [FilePath] and if so why can it not print the result?
lenAction=list>>=return . length
This just a type error.
The type of >>= (when used with IO) is
(>>=) :: IO a -> (a -> IO b) -> IO b
That is, the second argument must be a function that returns a value of type IO something. length doesn't have such a type, so the type checker complains.
As you show, return :: a -> IO a can be used to lift a value into IO. That is one way to solve the problem.
Another way is to use fmap:
fmap length listAction
This works because fmap (when used with IO) is
fmap :: (a -> b) -> IO a -> IO b
(Other ways of doing the same thing include liftM length listAction and length <$> listAction.)
Inside a do block of a ExceptT String IO ()
I have a function that produces a ReaderT like so:
type UDCEnv = (AWS.Env, Bool)
uploadVersionFilesToCaches :: S3.BucketName
-> FilePath
-> [GitRepoNameAndVersion]
-> ReaderT UDCEnv IO ()
I just so happen to have a Maybe FilePath so I create my ReaderT like so:
let maybeReader :: Maybe (ReaderT UDCEnv IO ()) =
uploadVersionFilesToCaches s3BucketName <$> maybeFilePath <*> Just gitRepoNamesAndVersions
I can even run the ReaderT like so:
let maybeIO :: Maybe (IO ()) =
runReaderT <$> maybeReader <*> Just (env, shouldIgnoreLocalCache, verbose)
Everything works fine as long as I use let expressions. As soon as I drop the let in the expression above to actually try to have expression evaluated Applicative gets types as ExceptT String IO FilePath instead of Maybe
The parts I am omitting are marked by ... :
f :: ... -> ExceptT String IO ()
f ... = do
...
runReaderT <$> maybeReader <*> Just (env, shouldIgnoreLocalCache, verbose) -- Error here
undefined
Produces
Couldn't match type ‘IO ()’ with ‘()’
Expected type: ReaderT UDCEnv IO () -> UDCEnv -> ()
Actual type: ReaderT UDCEnv IO () -> UDCEnv -> IO ()
In the first argument of ‘(<$>)’, namely ‘runReaderT’
In the first argument of ‘(<*>)’, namely
‘runReaderT
<$>
(uploadVersionFilesToCaches s3BucketName <$> maybeFilePath
<*> Just gitRepoNamesAndVersions)’
/Users/blender/Code/Personal/Haskell/Rome-Public/src/Lib.hs: 82, 73
Couldn't match type ‘Maybe’ with ‘ExceptT String IO’
Expected type: ExceptT String IO FilePath
Actual type: Maybe FilePath
In the second argument of ‘(<$>)’, namely ‘maybeFilePath’
In the first argument of ‘(<*>)’, namely
‘uploadVersionFilesToCaches s3BucketName <$> maybeFilePath’
I think the first error is because I'm missing some liftIO somewhere.
However I have no idea what to do about the misunderstood Applicative.
I could case analysis on the Maybe of course instead of using Applicative but I would really prefer not to.
Edit: Oops, fixed a bug.
There seems to be a minor inconsistency in your question, because the do-block you provide contains a runReaderT ... expression that doesn't match the expression given in your error message.
However, ultimately the problem is this: in a do-block of type m a for some monad m, each plain expression (and each right-hand side of an x <- y expression) has to have type m b for some b. So, by using your runReaderT ... expression in a do-block of type ExceptT String IO (), you're forcing Haskell to type-check it as ExceptT String IO a for some a. However, it's a Maybe (IO ()), so that type-checking will fail.
You'd get a similar error if you tried:
foo :: ExceptT String IO ()
foo = do Just (putStrLn "won't work") -- has type Maybe (IO ())
undefined
You need to decide how to adapt the runReaderT ... expression to the surrounding do-block. Two reasonable options are:
foo = do ...
maybe (throwError "reader was Nothing!") liftIO
$ runReaderT ...
undefined
which will throw an ExceptT-style error if your maybeReader is Nothing or:
foo = do ...
maybe (return ()) liftIO
$ runReaderT ...
undefined
which will do .. erm .. nothing in case of Nothing.
{-# LANGUAGE NoMonomorphismRestriction #-}
module Try where
f :: IO (a -> IO String)
f = return $ const getLine
main :: IO ()
main = do
g <- f
:: IO (a -> IO String)
g "String" >>= print
g 5 >>= print
Even with the NoMonomorphismRestriction flag and explicit type signature, this module fails to compile with Couldn't match expected type ‘[Char]’ with actual type ‘Int’, despite g being fully polymorphic.
This is not what the monomorphism restriction means. The monomorphism restriction says that if a definition has no type signature and has a left-hand side with no parameters, it will be specialized to a monomorphic type (or rather just monomorphic enough to get rid of any class constraints). You have given type signatures so it doesn't apply.
The problem here is that you have given the wrong type to f.
f :: IO (a -> IO String)
actually means
f :: forall a. IO (a -> IO String)
That is, first pick a type a, then you can bind to get a monomorphic function of type a -> IO String for that a. There is no trouble with this program, for example:
main = do
g <- f
g "String" >>= print
g' <- f
g' 5 >>= print
But your usage example requires this type:
f :: IO (forall a. a -> IO String)
That is, you want to bind first and pick the type later, i.e. use the function at multiple types. This is called an "impredicative type" and unfortunately GHC has not supported them for quite a while, as far as I know.
The way to solve this problem is to make a newtype wrapper that explicitly quantifies the inner polymorphic type:
newtype R = R { getR :: forall a. a -> IO String }
f :: IO R
f = return $ R (const getLine)
main :: IO ()
main = do
g <- f
getR g "String" >>= print
getR g 5 >>= print