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.)
Related
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.
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.
I'm working on a Haskell server using scotty and persistent. Many handlers need access to the database connection pool, so I've taken to passing the pool around throughout the app, in this sort of fashion:
main = do
runNoLoggingT $ withSqlitePool ":memory:" 10 $ \pool ->
liftIO $ scotty 7000 (app pool)
app pool = do
get "/people" $ do
people <- liftIO $ runSqlPool getPeople pool
renderPeople people
get "/foods" $ do
food <- liftIO $ runSqlPool getFoods pool
renderFoods food
where getPeople and getFoods are appropriate persistent database actions that return [Person] and [Food] respectively.
The pattern of calling liftIO and runSqlPool on a pool becomes tiresome after a while - wouldn't it be great if I could refactor them into a single function, like Yesod's runDB, which would just take the query and return the appropriate type. My attempt at writing something like this is:
runDB' :: (MonadIO m) => ConnectionPool -> SqlPersistT IO a -> m a
runDB' pool q = liftIO $ runSqlPool q pool
Now, I can write this:
main = do
runNoLoggingT $ withSqlitePool ":memory:" 10 $ \pool ->
liftIO $ scotty 7000 $ app (runDB' pool)
app runDB = do
get "/people" $ do
people <- runDB getPeople
renderPeople people
get "/foods" $ do
food <- runDB getFoods
renderFoods food
Except that GHC complains:
Couldn't match type `Food' with `Person'
Expected type: persistent-2.1.1.4:Database.Persist.Sql.Types.SqlPersistT
IO
[persistent-2.1.1.4:Database.Persist.Class.PersistEntity.Entity
Person]
Actual type: persistent-2.1.1.4:Database.Persist.Sql.Types.SqlPersistT
IO
[persistent-2.1.1.4:Database.Persist.Class.PersistEntity.Entity
Food]
In the first argument of `runDB', namely `getFoods'
It seems like GHC is saying that in fact the type of runDB becomes specialised somehow. But then how are functions like runSqlPool defined? Its type signature looks similar to mine:
runSqlPool :: MonadBaseControl IO m => SqlPersistT m a -> Pool Connection -> m a
but it can be used with database queries that return many different types, as I was doing originally. I think there's something fundamental I'm misunderstanding about types here, but I have no idea how to find out what it is! Any help would be greatly appreciated.
EDIT:
at Yuras' suggestion, I've added this:
type DBRunner m a = (MonadIO m) => SqlPersistT IO a -> m a
runDB' :: ConnectionPool -> DBRunner m a
app :: forall a. DBRunner ActionM a -> ScottyM ()
which required -XRankNTypes for the typedef. However, the compiler error is still identical.
EDIT:
Victory to the commentors. This allows the code to compile:
app :: (forall a. DBRunner ActionM a) -> ScottyM ()
For which I'm grateful, but still mystified!
The code is currently looking like this and this.
It seems like GHC is saying that in fact the type of runDB becomes specialised somehow.
Your guess is right. Your original type was app :: (MonadIO m) => (SqlPersistT IO a -> m a) -> ScottyM (). This means that your runDB argument of type SqlPersistT IO a -> m a can be used at any one type a. However, the body of app wants to use the runDB argument at two different types (Person and Food) so instead we need to pass an argument that can work for any number of different types in the body. Thus app needs the type
app :: MonadIO m => (forall a. SqlPersistT IO a -> m a) -> ScottyM ()
(I would suggest keeping the MonadIO constraint outside the forall but you can also put it inside.)
EDIT:
What's going on behind the scenes is the following:
(F a -> G a) -> X means forall a. (F a -> G a) -> X, which means /\a -> (F a -> G a) -> X. /\ is the type-level lambda. That is, the caller gets to pass in a single type a and a function of type F a -> G a for that particular choice of a.
(forall a. F a -> G a) -> X means (/\a -> F a -> G a) -> X and the caller has to pass in a function which the callee can specialise to many choices of a.
Lets play the game:
Prelude> let f str = (read str, read str)
Prelude> f "1" :: (Int, Float)
(1,1.0)
Works as expected.
Prelude> let f str = (read1 str, read1 str) where read1 = read
Prelude> f "1" :: (Int, Float)
(1,1.0)
Works too.
Prelude> let f read1 str = (read1 str, read1 str)
Prelude> f read "1" :: (Int, Float)
<interactive>:21:1:
Couldn't match type ‘Int’ with ‘Float’
Expected type: (Int, Float)
Actual type: (Int, Int)
In the expression: f read "1" :: (Int, Float)
In an equation for ‘it’: it = f read "1" :: (Int, Float)
But this doesn't. What the difference?
The last f has the next type:
Prelude> :t f
f :: (t1 -> t) -> t1 -> (t, t)
So it doesn't work for clear reason, both elements of the tuple should have the same type.
The fix is like that:
Prelude> :set -XRankNTypes
Prelude> let f read1 str = (read1 str, read1 str); f :: (Read a1, Read a2) => (forall a . Read a => str -> a) -> str -> (a1, a2)
Prelude> f read "1" :: (Int, Float)
(1,1.0)
Unlikely I can come with good explanation of RankNTypes, so I'd not even try. There is enough resources in web.
To really answer the title question that apparently continues to mystify you: Haskell always chooses the most generic rank-1 type for a function, when you don't supply an explicit signature. So for app in the expression app (runDB' pool), GHC would attempt to have type
app :: DBRunner ActionM a -> ScottyM ()
which is in fact shorthand for
app :: forall a. ( DBRunner ActionM a -> ScottyM () )
This is rank-1 polymorphic, because all type variables are introduced outside of the signature (there is no quantification going on in the signature itself; the argument DBRunner ActionM a is in fact monomorphic since a is fixed at that point). Actually, it is the most generic type possible: it can work with a polymorphic argument like (runDB' pool), but would also be ok with monomorphic arguments.
But it turns out the implementation of app can't offer that generality: it needs a polymorphic action, otherwise it can't feed two different types of a values to that action. Therefore you need to manually request the more specific type
app :: (forall a. DBRunner ActionM a) -> ScottyM ()
which is rank-2, because it has a signature which contains a rank-1 polymorphic argument. GHC can't really know this is the type you want – there's no well defined “most general possible rank-n type” for an expression, since you can always push in extra quantifiers. So you must manually specify the rank-2 type.
How can a output a list of strings using putStr and map? I want to do something like:
s=["test1","test2"]
map putStr s
But have no experience with monads and don't know how to get it right...
Any kind of hint is enormously welcome!
The correct thing to do here is use mapM_ :: Monad m => (a -> m b) -> [a] -> m (). But if you'd like to learn a little about some useful Prelude functions...
We have two functions here,
map :: (a -> b) -> [a] -> [b]
putStr :: String -> IO ()
if we substitute the type of putStr into map (ie. a ~ String, b ~ IO ()) we get
map putStr :: [String] -> [IO ()]
so this takes a list of strings and gives us a list of IO () actions; IO computations that don't return anything usefull.
We'd like to turn that [IO ()] into something like IO (...) because we need IO to be the outermost layer in order to use it in main :: IO ()
The function sequence :: Monad m => [m a] -> m [a] from the Prelude is exactly what we need. It takes a list of monadic actions, executes them and returns the results in a list wrapped in the monad. Intuitively it moves the monad to the outermost layer.
sequence . map putStr :: [String] -> IO [()]
This is quite close, but we still have IO [()] instead of IO (), we don't really care about the [()] result and it'd be nice to ignore it. Again, the Prelude has what we need: sequence_ :: Monad m => [m a] -> m () which executes each monadic action in the list and ignores their return values.
Note that mapM_ is defined as
mapM_ f = sequence_ . map f
The type of map is:
map :: (a -> b) -> [a] -> [b]
this means that, since putStr returns an IO (), your expression will return a [IO ()].
You can, instead, use mapM_ (imported from Prelude) that will ignore the return type from the mapping and return an IO () which is a suitable return type for main:
main = do
let s=["test1","test2"]
mapM_ putStr s
Live demo
I'm trying to wrap my head around Haskell's monads by actually trying to manually implement some of the used operators, like >>= (bind). I just do this to try and get a better understanding of things, there is no practical use in this endeavor. But I get to a brick wall from the get go because code that appears like "code examples" in explanations of monads turn out not to be actually compilable code...
When I try to define my own bind I get a type error. This code:
bind :: IO a -> (a -> IO b) -> IO b
(bind action1 action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in
(b, world2)
...gives me these (9 is the first line of the code above):
iom.hs:10:1:
Couldn't match expected type `t0 -> (t1, t2)'
with actual type `IO b'
The equation(s) for `bind' have three arguments,
but its type `IO a -> (a -> IO b) -> IO b' has only two
iom.hs:11:23:
Couldn't match expected type `t0 -> (t1, t2)'
with actual type `IO a'
The function `action1' is applied to one argument,
but its type `IO a' has none
In the expression: action1 world0
In a pattern binding: (a, world1) = action1 world0
iom.hs:12:23:
Couldn't match expected type `t2 -> (t0, t1)'
with actual type `IO b'
The function `action2' is applied to two arguments,
but its type `a -> IO b' has only one
In the expression: action2 a world1
In a pattern binding: (b, world2) = action2 a world1
I am just starting with an example from http://www.haskell.org/haskellwiki/IO_inside , just replacing >>= with bind to prevent a name clash and defining it as a function not infox operator. This is the example provided as an explanation (copy pasted):
(>>=) :: IO a -> (a -> IO b) -> IO b
(action1 >>= action2) world0 =
let (a, world1) = action1 world0
(b, world2) = action2 a world1
in (b, world2)
Now if I replace the last line with return (b, world2) I get this instead:
iom.hs:10:1:
Couldn't match expected type `t0 -> m0 (t1, t2)'
with actual type `IO b'
The equation(s) for `bind' have three arguments,
but its type `IO a -> (a -> IO b) -> IO b' has only two
iom.hs:11:23:
Couldn't match expected type `t0 -> (t1, t2)'
with actual type `IO a'
The function `action1' is applied to one argument,
but its type `IO a' has none
In the expression: action1 world0
In a pattern binding: (a, world1) = action1 world0
iom.hs:12:23:
Couldn't match expected type `t2 -> (t0, t1)'
with actual type `IO b'
The function `action2' is applied to two arguments,
but its type `a -> IO b' has only one
In the expression: action2 a world1
In a pattern binding: (b, world2) = action2 a world1
That happens because the IO inside tutorial uses a fictional (though inspired by the real thing) definition for IO (see section 3 for details):
type IO a = RealWorld -> (a, RealWorld)
Using this definition, IO a is actually a synonym for a function type, and so the extra argument your error messages refer to wouldn't be a problem. The actual IO type is abstract, so you can't see or use in your code what it is actually made of.
If you want to run the article code yourself, use that definition while renaming IO to e.g. MyIO and change your other definitions accordingly.
As they say in the article, that definition of IO is actually not correct. IO is not a type synonym for a function type, it's a bit more mysterious than that. The code given only works if IO is a type synonym for a function because, if not, you are trying to specify three arguments for a function whose signature says it takes two arguments.
There is a Monad that works much more like what they are describing called State.