Compile error while generalizing function - complex error message - haskell

I had a function that runs in IO monad:
withDB :: (forall c. IConnection c => c -> IO b) -> IO b
withDB fn = bracket (connectSqlite3 "int/db.sqlite3") disconnect fn
And now I decided to generalize it to run in some MonadIO m. I did it following way, re-inventing bracket with my scope (do you know some from library?):
scope :: MonadIO m => m a -> (a -> m b) -> (a -> m c) -> m c
scope before after action = do
x <- before
r <- action x
_ <- after x
return r
withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b
withDB fn = liftIO $ scope
(liftIO $ connectSqlite3 "int/db.sqlite3")
(\x -> liftIO $ disconnect x) fn
I got the error:
Could not deduce (m ~ IO)
from the context (MonadIO m)
bound by the type signature for
withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b
at src\...
'm' is a rigid type variable bound by
the signature for
withDB :: MonadIO m => (forall c. IConnection c => c -> m b) -> m b
Expected type: IO b
Actual type: m b
In the third argument of 'scope' namely 'fn'
In the second argument of '($)', namely
'scope
(liftIO $ connectSqlite3 "int/db.sqlite3")
(\x -> liftIO $ disconnect x)
fn'
And now my questions:
What does mean m ~ IO? What first two lines of error say? Also, I saw this ~ construction in haskell code but can't find what is it. Extension? What is rigid type variable?
I found error and fixed it. It's enough to remove liftIO $ before scope. But it was just try-recompile cycle. Where in this error message told about the place of the error? I see something wrong with 'fn'. OK, I thought about it a bit and have a guess: GHC infers type from top to bottom. And it inferred from using liftIO that m should be IO but fn has general type m so it is error. Does any haskell compiler infers from top to bottom? And (more important) can I see types that GHC infers for sub expressions in output?
Thank you for reading this long question!

liftIO :: (MonadIO m) => IO a -> m a takes an IO action, so by saying liftIO $ scope ..., you're saying that scope ... must have the type IO b. That means that the arguments to scope must use the IO monad. Since your use of scope ensures that m must be IO, you can think of scope as having this type in context:
scope :: IO a -> (a -> IO b) -> (a -> IO c) -> IO b
Because of this, the liftIOs inside the scope call do nothing; they're merely converting from IO a to IO a, and you can't use fn, because it works in m, not IO. Removing the liftIO fixed it because it ran scope directly inside m, rather than running it in IO (impossible, because fn runs in m) and lifting that action into m.

Related

Haskell async: Using different types of Monads in withAsync

I'm writing a Monad transformer, and to run the transformer I need to spawn an IO thread that writes elements to a TQueue that the main thread has access to. However, none of the versions of withAsync provide the right signature. I need one with a signature something like
(MonadIO m) => IO a -> (Async a -> m b) -> m b
I tried lifted-async, but that had a lot of restrictions and had both the main and spawned thread in the same Monad.
Alternatively, since Control.Concurrent.Async says that withAsync is equivalent to
withAsync action inner = mask $ \restore -> do
a <- async (restore action)
restore (inner a) `finally` uninterruptibleCancel a
An alternate solution would be a version of mask that has a type signature something like
mask :: (MonadMask m) => ((forall a n. (MonadMask n) => n a -> n a) -> m b) -> m b
The closest I came to a solution was the following code, which I'm pretty sure doesn't work, but I can't be sure since I don't fully understand how mask works.
withAsyncT :: (MonadIO m, MonadMask m) => IO a -> (Async a -> m b) -> m b
withAsyncT action inner = mask $ \restore -> do
a <- liftIO $ asyncWithUnmask (\rstr -> rstr action)
restore (inner a) `finally` (liftIO $ uninterruptibleCancel a)
Is there a reasonable solution to get such a withAsync? Or do I have to use something like unliftio?

When is a generic function not generic?

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.

Haskell: Confused about type of `>>=` operator

I'm working my way through some introductory Haskell materials and am currently going through Monads. I conceptually understand that the >>= operator is of type:
(Monad m) => m a -> (a -> m b) -> m b.
In that context, I'm confused as to why the following code works, i.e., why it doesn't result in a type mismatch:
main = getLine >>= \xs -> putStrLn xs
Since we know that getLine :: IO String, I'd assume that it can be 'bound' with a function of type String -> IO String. However putStrLn is of a different type: putStrLn :: String -> IO ().
So why does Haskell allow us to use >>= with these two functions?
Let's just line up the types:
(>>=) :: m a -> ( a -> m b) -> m b
getLine :: IO String
putStrLn :: (String -> IO ())
Here we have m = IO, a = String, and b = (), so we can substitute these into >>='s type signature to get a final type signature of
(>>=) :: IO String -> (String -> IO ()) -> IO ()
() is a valid type (called unit, note that it contains only one possible non-bottom value) and in the definition would be b.
a = String and b = () thus we get:
IO String -> (String -> IO ()) -> IO ()
Since we know that getLine :: IO String, I'd assume that it can be 'bound' with a function of type String -> IO String.
Why would you think that? Look again at the type signature:
(>>=) :: m a -> (a -> m b) -> m b
Thing in the left is m a, thing on the right is m b. Most particularly the bit in the middle, a -> m b, says that the function you pass to >>= takes an a and returns an m b. It doesn't say it has to return m a, it says it can be m b, where b is any random type. It doesn't have to match a.
In your example, the lambda function takes a String and returns an IO (). So a = String and b = (). And that's fine.

Lift a function and its argument to a different monadic context

I am not sure how to formulate this question scientifically exact, so I am just going to show you an example.
I am using state in a StateT transformer. Underlying is IO. Inside the StateT IO operation I need to use alloca. However, I can't lift alloca to StateT IO because it expects an argument of type (Ptr a -> IO a) while I require it to work with an argument of (Ptr a -> StateT IO MyState a).
(However, this is a generic question about monad transformers rather than specific to IO, StateT or alloca.)
I came up with the following, working solution:
-- for reference
-- alloca :: (Storable a) => (Ptr a -> IO b) -> IO b
allocaS :: (Storable a) => (Ptr a -> StateT s IO b) -> StateT s IO b
allocaS f = do
state <- get
(res, st) <- liftIO $ alloca $ \ptr -> (runStateT (f ptr) state)
put st
return res
However, it seems wrong to me that I should have to de- and reconstruct the StateT action in order to use it with alloca. Also, I have seen this pattern in some variations more than once and it's not always as simple and safe as here with StateT.
Is there a better way to do this?
This can be accomplished using MonadBaseControl in monad-control, which has been devised exactly for this purpose:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad
import Control.Monad.Trans.Control
import qualified Foreign.Ptr as F
import qualified Foreign.Marshal.Alloc as F
import qualified Foreign.Storable as F
alloca :: (MonadBaseControl IO m, F.Storable a) => (F.Ptr a -> m b) -> m b
alloca f = control $ \runInIO -> F.alloca (runInIO . f)
This enhanced version of alloca can be used with any monad stack based on IO that implements MonadBaseControl, including StateT s IO.
Instances of MonadBaseControl allow their monadic values to be encoded in the base monad (here IO), passed to a function in the base monad (like F.alloca) and then reconstruct them back.
See also What is MonadBaseControl for?
Package lifted-base contains many of the standard IO functions lifted to MonadBaseControl IO, but alloca isn't (yet) among them.
Good afternoon,
AFAIK, there is no general way to turn a function of type (a -> m b) -> m b into (a -> t m b) -> t m b because that would imply the existence of a function of type MonadTrans t => (a -> t m b) -> (a -> m b).
Such a function cannot possibly exist, since most transformers cannot be stripped so easily from a type signature (how do you turn a MaybeT m a into an m a for all a ?). Hence, the most general way to turn (a -> m b) -> m b to (a -> t m b) -> t m b is undefined.
In the case of StateT s m, there is a loophole that allows you to define it anyway. Since StateT s m a === s -> m (s,a), we can rewrite the type equation to :
(a -> StateT s m b) -> StateT s m b
=== (a -> s -> m (s,b)) -> s -> m (s,b)
=== s -> (s -> (a -> m (s,b)) -> m (s,b) -- we reorder curried arguments
=== s -> (s -> (A -> m B)) -> m B -- where A = a, B = (s,b)
Solving this new type signature is now trivial :
liftedState f s run = f (run s)
allocaS :: Storable a => (Ptr a -> StateT IO b) -> StateT IO b
allocaS = isomorphic (liftedState alloca)
That is about the best we can do in terms of code reuse, short of defining a new subclass of MonadTrans for all monads that exhibit the same behaviour.
I hope I made myself clear enough (I didn't want to go into too much detail for fear of being confusing)
Have an excellent day :-)

Rebinding do notation for indexed monads

I was following Conor McBride's "Kleisli arrows of outrageous fortune" paper and I've posted my implementation of his code here. Briefly, he defines the following types and classes:
type a :-> b = forall i . a i -> b i
class IFunctor f where imap :: (a :-> b) -> (f a :-> f b)
class (IFunctor m) => IMonad m where
skip :: a :-> m a
bind :: (a :-> m b) -> (m a :-> m b)
data (a := i) j where
V :: a -> (a := i) i
Then he defines two types of binds, the latter of which uses (:=) to restrict the initial index:
-- Conor McBride's "demonic bind"
(?>=) :: (IMonad m) => m a i -> (a :-> m b) -> m b i
(?>=) = flip bind
-- Conor McBride's "angelic bind"
(>>=) :: (IMonad m) => m (a := j) i -> (a -> m b j) -> m b i
m >>= f = bind (\(V a) -> f a) m
The latter bind works perfectly fine for rebinding do notation to use indexed monads with the RebindableSyntax extension, using the following corresponding definitions for return and fail:
return :: (IMonad m) => a -> m (a := i) i
return = skip . V
fail :: String -> m a i
fail = error
... but the problem is that I cannot get the former bind (i.e. (?>=)) to work. I tried instead defining (>>=) and return to be:
(>>=) :: (IMonad m) => m a i -> (a :-> m b) -> m b i
(>>=) = (?>=)
return :: (IMonad m) => a :-> m a
return = skip
Then I created a data type guaranteed to inhabit a specific index:
data Unit a where
Unit :: Unit ()
But when I try to rebind do notation using the new definitions for (>>=) and return, it does not work, as demonstrated in the following example:
-- Without do notation
test1 = skip Unit >>= \Unit -> skip Unit
-- With do notation
test2 = do
Unit <- skip Unit
skip Unit
test1 type-checks, but test2 does not, which is weird, since I thought all that RebindableSyntax did was let do notation desugar test2 to test1, so if test1 type-checks, then why does not test2? The error I get is:
Couldn't match expected type `t0 -> t1'
with actual type `a0 :-> m0 b0'
Expected type: m0 a0 i0 -> (t0 -> t1) -> m Unit ()
Actual type: m0 a0 i0 -> (a0 :-> m0 b0) -> m0 b0 i0
In a stmt of a 'do' block: Unit <- skip Unit
In the expression:
do { Unit <- skip Unit;
skip Unit }
The error remains even when I use the explicit forall syntax instead of the :-> type operator.
Now, I know there is another problem with using the "demonic bind", which is that you can't define (>>), but I still wanted to see how far I could go with it. Can anybody explain why I cannot get GHC to desugar the "demonic bind", even when it would normally type-check?
IIUC, the GHC desugarer actually runs after the typechecker (source). That explains why the situation you observe is theoretically possible. The typechecker probably has some special typing rules for the do-notation, and those may be inconsistent with what the typechecker would do with the desugarred code.
Of course, it's reasonable to expect them to be consistent, so I would recommend filing a GHC bug.

Resources