This code is compiling:
trigger :: (Typeable n) => n () -> IO ()
trigger n = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
But if I add parenthesis it doesn't anymore:
-- not compiling
trigger' :: (forall n. (Typeable n) => n ()) -> IO ()
trigger' n = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
It says "No instance for (Typeable n) arising from a use of ‘cast’".
Why?
In practice I need the second form because in my program I use data types that have forall:
data Foo = Foo {a :: (Typeable n) => n ()}
trigger' :: Foo -> IO ()
trigger' (Foo n) = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
trigger :: (Typeable n) => n () -> IO ()
"For any n that is Typeable, I can turn a n () into an IO ()
trigger' :: (forall n. (Typeable n) => n ()) -> IO ()
"I can turn a value which can typed n () for every n that is Typeable into an IO ()"
The second doesn't really make sense; there are no values of the required type to give it (other than undefined). Even if there were, there would be no need to use cast; since the argument can be assigned every n () type you can just return it as is; it's already an IO () as a special case of being every n (), so you shouldn't even need trigger (except perhaps to pin down an ambiguous type). This is almost certainly not what you want.
The first signature is what you would need if you had an existential type; if you can build Foos containing any n () (provided it's Typeable), and you later want to be able to run them if they happen to contain an IO (). I suspect that this is actually what you want, and you have accidentally defined your data type incorrectly, rather than the problem being in trigger.
Consider this:
{-# LANGUAGE GADTs, RankNTypes #-}
module Foo where
import Data.Typeable (Typeable, cast)
data Foo = Foo { a :: Typeable n => n () }
data Bar = Bar { bar :: forall n. Typeable n => n () }
data Baz = forall n. Typeable n => Baz { baz :: n () }
trigger :: Typeable n => n () -> IO ()
trigger n = case (cast n) of
Just n' -> n'
Nothing -> error "n is not IO"
Your Foo is equivalent to Bar, not to Baz:
λ :t Foo
Foo :: (forall (n :: * -> *). Typeable n => n ()) -> Foo
*Foo
λ :t Bar
Bar :: (forall (n :: * -> *). Typeable n => n ()) -> Bar
*Foo
λ :t Baz
Baz :: Typeable n => n () -> Baz
The implicit forall is placed at the beginning of the type signature of the field a, rather than quantifying over the whole constructor. Consequently you've asked that the field a is forall n. Typeable n => n (), instead of the constructor working on any n () and the field containing some particular but unknown n ().
And we can see that Baz works with trigger the way you seem to want it to:
*Foo
λ case Baz (putStrLn "yay") of Baz x -> trigger x
yay
it :: ()
While for Foo, if you can get its field out you can use it directly as an IO () without needing any cast, but there's no way to actually put something into a Foo in the first place:
λ case Foo undefined of Foo x -> putStrLn "weird" >> x
weird
*** Exception: Prelude.undefined
Because "weird" is actually printed you can see that the type-checker accepted this expression; it was perfectly fine with the idea that the hypothetical contents of a Foo would be usable as an IO (), with no need for trigger at all. But I've handwaved-away the problem actually constructing a Foo in the first place; it's basically impossible without using something like undefined. This is what makes me think it's your data declaration that is incorrect, rather than trigger.
But if there's something about your real use-case that makes the data type wrapping a polymorphic field more reasonable (if there were more constraints than Typeable it could be possible to use), and it also doesn't work to simply dispense with trigger and make use of the contained polymorphism to "cast" to a more specific type, then we'll need more information to address your actual use case.
Related
Based on this QA;
A new type for all a (normal value) and nothing in Haskell?
Here's the first working code:
import Data.Typeable (Typeable, cast)
data None = None
none :: None
none = None
f :: Typeable a => a -> a
f = \x ->
case (cast x :: Maybe None) of
Just None -> undefined
Nothing -> x
test :: IO ()
test = do
print $ f (5 :: Int) --5
pure $ f none
print "done"
This code works as I intended.
Now, I want to make this optionMap and here is the non-working code with an error:
import Data.Typeable (Typeable, cast)
data None = None
none :: None
none = None
optionMap :: Typeable a => (a -> b) -> a -> b
optionMap = \f -> \x ->
case (cast x :: Maybe None) of
Just None -> undefined
Nothing -> f x
test :: IO ()
test = do
print $ optionMap (\a -> a + 1) (5 :: Int) -- 6
pure $ optionMap (\a -> a + 1) none
-- No instance for (Num None) arising from a use of ‘+’
print "done"
Surely, I understand the meaning of the error, however, I expect it pattern-matched as Just None as the first working example, then the value should be undefined.
Is it possible to fix?
Well, I specifically said in my answer to your other question that Typeable isn't a good tool to address this goal. But sure, let's see if we can fix it.
For starters, this is pretty silly:
optionMap :: Typeable a => (a -> b) -> a -> b
optionMap = \f -> \x ->
case (cast x :: Maybe None) of
Just None -> undefined
Nothing -> f x
If the function f can handle arguments of type a, and we have an x of type a, there's no need to check for None at all; f x is valid. If you really want optionMap, you don't want it to have this type.
To see why, let's look at the way you were calling it. With optionMap (\a -> a + 1) none you need all these types to unify:
(\a -> a + 1) :: Num t => t -> t
none :: None
optionMap :: Typeable a => (a -> b) -> a -> b
I've lined them up (and use globally unique names for type variables) to make it clear what type inference is going to happen. We're going to decide that a ~ t, and b ~ t, but also that a ~ None. If any of these facts were not true, we would have a type error. So GHC assumes they are all true, but that gives us as a combined type of optionMap:
optionMap :: (Typeable None, Num None) => (None -> None) -> None -> None
Not very helpful! The whole point of None was that the function wouldn't be applied if it was None, so we don't want any of the constraints from the function to end up as constraints on the a when we pass a None in that position. The only way we can achieve that is by having the type of the function parameter's parameter be a different type variable from the second parameter of optionMap. So:
optionMap :: (Typeable a, Typeable a') => (a' -> b) -> a -> b
But now optionMap accepts any type a, not just the a' values that can be passed to its function parameter. What we need is to then use cast the other way around from the way you used it; check if it's safe to pass x to f and handle that specially (by returning f x), and put our do-nothing case in the branch where the cast failed. Something like:
optionMap :: forall a a' b. (Typeable a, Typeable a') => (a' -> b) -> a -> b
optionMap f x =
case (cast x :: Maybe a') of
Just x' -> f x'
Nothing -> undefined
Note that doing that requires the ScopedTypeVariables extension (otherwise in the cast x :: Maybe a' the a' is a new local type variable, not a reference to the a' from optionMap's signature), and requires we explicitly introduce the type variables with forall (that's how you declare a larger-scope type variable that can be referenced from more than one signature, with ScopedTypeVariables).
Lets try it:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Typeable
data None = None
none = None
optionMap :: forall a' a b. (Typeable a, Typeable a') => (a' -> b) -> a -> b
optionMap f x =
case (cast x :: Maybe a') of
Just x' -> f x'
Nothing -> undefined
main :: IO ()
main = do
print $ optionMap (\a -> a + 1) (5 :: Int)
pure $ optionMap (\a -> a + 1) none
print "done"
Unfortunately we get a bunch of messages like:
foo.hs:17:3: warning: [-Wdeferred-type-errors]
• Ambiguous type variable ‘a0’ arising from a use of ‘print’
prevents the constraint ‘(Show a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
With the Typeable constraints, GHC is no longer willing to default the ambiguous types to Integer. Plus we've disconnected the type of the function parameter and the other paramter to optionMap, so the explicit annotation in (5 :: Int) tells us nothing about the type of the function (which we need to know in order for cast to tell us whether 5 :: Int is the same type!), so it' probably good that GHC won't default types of the functions or it would have picked Integer -> Integer, which doesn't match. I frequently try to use Bool or Char in these small test programs rather than numbers, for exactly this reason; Num and defaulting just make everything a little more complicated. But ets try that again with more types:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Typeable
data None = None
none = None
optionMap :: forall a' a b. (Typeable a, Typeable a') => (a' -> b) -> a -> b
optionMap f x =
case (cast x :: Maybe a') of
Just x' -> f x'
Nothing -> undefined
main :: IO ()
main = do
print $ optionMap (\a -> a + (1 :: Int)) (5 :: Int)
pure $ optionMap (\a -> a + (1 :: Int)) none
print "done"
Great it compiles, and if we run it:
❯ runhaskell foo.hs
6
"done"
But there's a problem:
main :: IO ()
main = do
print $ optionMap (\a -> a + (1 :: Int)) (5 :: Int)
pure $ optionMap (\a -> a + (1 :: Int)) 'a'
pure $ optionMap (\a -> a + (1 :: Int)) True
pure $ optionMap (\a -> a + (1 :: Int)) (5 :: Integer)
pure $ optionMap (\a -> a + (1 :: Int)) (print False)
print "done"
All of these also happily take the non-equal types path. We haven't written optionMap so that it accepts either None or the type that matches the function's argument, we've made it accept any type at all, and applies the function if the types happen to match. Which means the type checker is completely unable to detect any mistakes we make. Any mistake where we intended the types to match but we messed up will be silently ignored.
We can try explicitly handling None:
optionMap :: forall a' a b. (Typeable a, Typeable a') => (a' -> b) -> a -> b
optionMap f x =
case (cast x :: Maybe a') of
Just x' -> f x'
Nothing ->
case (cast x :: Maybe None) of
Just None -> undefined
Nothing -> error "what do we put here?"
But no matter how many layers deep of cast and case we go, we're going to be left with that final "it could be absolutely anything else" case; we don't end up with a clean optionMap that accepts the correct type to apply the function or None, and nothing else.
What we need is a way to make a type for that second parameter that isn't just a universal a, but rather is a specific type for None or a'. That's exactly what you asked the other question about, and is exactly what I said Typeable wouldn't really help you do.
Haskell of course has an extremely uncomplicated and easy way of representing the choice between two types: discriminated unions, AKA algebraic data types with multiple constructors. That's exactly what you have already rejected because "the complication that deliver to code is far from justified at least for my purpose".
Before I address how you can actually address your need, I need to come back to another problem with your attempt: why are you using undefined? undefined does not "do nothing"; it isn't a neutral value can use when you haven't got what you need, as null is often used in other programming languages. When Haskell hits a place you've written undefined it terminates the program with an error message. I suspect you know that, because you didn't write print in the line of your test program that was supposed to handle the none example of using optionMap, you wrote pure.
pure of course wraps up the result in an IO Int, but because we do anything with it nothing actually happens. We'd get exactly the same result from:
main :: IO ()
main = do
print $ optionMap (\a -> a + (1 :: Int)) (5 :: Int)
pure $ error "didn't run"
print "done"
So it's a pretty pointless test of optionMap. What actually happens if we try to use it, changing the pure to a print:
❯ runhaskell foo.hs
6
foo.hs: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
undefined, called at foo.hs:12:16 in main:Main
Using optionMap _ none isn't a type error, but if it ever actually gets evaluated it terminates the program with an error. If we were happy with that we could skip this whole attempt and just use:
print $ (\a -> a + 1) 5
print $ (\a -> a + 1) undefined
Because undefined is a member of every type (along with infinite loops, and error "whatever", and a whole host of other ways of writing the bottom value), we can already just pass a "wrong" value when we haven't got a real one, if we're happy with this "error out at runtime" behaviour (which I personally am not).
So even if we could put in even more work and somehow get a type for optionMap that works the way you want, it still doesn't give you the ability to feed in a none to a computation expecting some other type and have the program ignore it. To do that you'd need to also have some way of "mapping" print across your "might be nothing, might be a value"; making it apply print (or any other IO action) if the value is there, and just do nothing if the value was None. Something like:
main :: IO ()
main = do
print `ioOption` optionMap (\a -> a + 1) (Some 5)
print `ioOption` optionMap (\a -> a + 1) None
print "done"
Where I've just swapped out the $ operator, which was directly applying print to the result of optionMap, for some new ioOption that applies print to the result of optionMap only if it is not None. You'll have just as much trouble implementing that as you do implementing optionMap.
Now, I know you said that using discriminated unions just added too much complexity to your code for you to bother with for your present purposes (although you seem to be willing to put in far more effort to avoid Maybe than you are to put into using it). But for completeness, I am going to show how these problems are solved using discriminated unions, in all their horrible "complexity":
data Option a = Some a | None
optionMap :: (a -> b) -> Option a -> Option b
optionMap _ None = None
optionMap f (Some x) = Some (f x)
ioOption :: (a -> IO ()) -> Option a -> IO ()
ioOption _ None = pure ()
ioOption f (Some x) = f x
main :: IO ()
main = do
print `ioOption` optionMap (\a -> a + 1) (Some 5)
print `ioOption` optionMap (\a -> a + 1) None
print "done"
And running this:
❯ runhaskell bar.hs
6
"done"
Things to note here:
The types are much simpler, and say exactly what they do; we still get the full power of Haskell's error checking (try changing Some 5 to Some True)
The implementation of optionMap is much simpler than any of our attempts with Typeable; we just write a case for None and a case for Some and we're done, which is about the minimal number of moving parts it would be possible to use
That's just about the only possible implementation we can give for optionMap. Haskell is (almost) forcing us to write correct code. (The only other things we could do that would pass the typechecker are return Nothing even in the Some case, return bottom/undefined, return bottom/undefined inside the Some constructor, or start messing with unsafe functions; all of those are obviously incorrect, so it's not hard to avoid accidentally writing any of them)
No extensions needed
We don't have to explicitly type annotate our numbers; Haskell's normal defaulting mechanism just works for us again
Of course, everything above (other than main) is already implemented for you. So you don't even need to write Option, optionMap, or ioOption. Option is Maybe, of course, optionMap is fmap, and ioOption is mapM_ from Data.Foldable (or traverse_ - they're the same thing only traverse_ has a more general type; they both exist for historical reasons). So we can just write:
import Data.Foldable
main :: IO ()
main = do
print `mapM_` fmap (\a -> a + 1) (Just 5)
print `mapM_` fmap (\a -> a + 1) Nothing
print "done"
Basically, whatever complexity you think you're avoiding by avoiding Maybe (or other discriminated unions), I guarantee you the alternatives are more complicated. You may just be not seeing how you can use the interfaces of Maybe (and other types) to write simpler code, in which case I'd be happy to answer further questions here. If you really just don't like writing constructor tags so much that things along the lines of our attempts at optionMap here seem preferable (if they could work), then you're really not going to enjoy using Haskell; it is a fundamental part of the language.
For the sake of completeness, I did think of one way to get an optionMap that really accepts "a function and either a value that can be passed to the function or None". It's not pretty, but it does work:
{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies #-}
data None = None
type family OptionResult a b
where OptionResult None _ = None
OptionResult _ b = b
class OptionMap a b a'
where optionMap :: (a -> b) -> a' -> OptionResult a' b
instance OptionMap a b None
where optionMap _ _ = None
instance (OptionResult a b ~ b) => OptionMap a b a
where optionMap f x = f x
class OptionTraverse a a'
where optionTraverse :: Applicative f => (a -> f ())-> a' -> f ()
instance OptionTraverse a None
where optionTraverse _ _ = pure ()
instance OptionTraverse a a
where optionTraverse f x = f x
main :: IO ()
main = do
(print :: Int -> IO ()) `optionTraverse` optionMap (\a -> a + (1 :: Int)) (5 :: Int)
(print :: Int -> IO ()) `optionTraverse` optionMap (\a -> a + (1 :: Int)) None
print "done"
The problem with this approach is it destroys type inference. GHC needs to know the type of everything very precisely in order to resolve the instance and type family we're using. GHC won't use the input and output types of the various function's we're using to constrain each other's types because we don't directly connect them with each other; if their types were wrong we wouldn't get a type-mismatch error, we'd get an error about it being unable to resolve the type class. And thus any polymorphic thing (like print) can't have its type resolved by connecting with something else; we have to explicitly provide a type annotation.
So this approach is not something I recommend. Programming with it with any scale will be very difficult. I include it only to show you the lengths you have to go to get your idea of how this should be done to work in Haskell, as compared to the extremely simple example using Maybe that does what you want, just not How you wanted it to work. (I would argue that the "how" of the Maybe approach is also a better way for it to work, but that is subject to taste, so I won't say that is definitive; I think you will have to agree it is simpler and easier to use, however)
So I've been playing around with MonadState class and I have encountered something I consider very strange.
I can try to write a monad like the following:
test ::
( MonadState Int m
, MonadState Bool m
)
=> m ()
test = do
((+1) <$> get) >>= put
(not <$> get) >>= put
If we compile this in ghc 8.6.4 we get the following:
MonadTrans.hs:10:13: error:
• Couldn't match type ‘Int’ with ‘Bool’
arising from a functional dependency between constraints:
‘MonadState Bool m’
arising from a use of ‘get’ at MonadTrans.hs:10:13-15
‘MonadState Int m’
arising from the type signature for:
test :: forall (m :: * -> *).
(MonadState Int m, MonadState Bool m) =>
m ()
at MonadTrans.hs:(4,1)-(8,11)
• In the second argument of ‘(<$>)’, namely ‘get’
In the first argument of ‘(>>=)’, namely ‘((+ 1) <$> get)’
In a stmt of a 'do' block: ((+ 1) <$> get) >>= put
|
10 | ((+1) <$> get) >>= put
|
(older versions of GHC for example 8.2.2 are actually fine with this and compile. I have no idea why.)
Ok this makes sense since the declaration of MonadState has a dependency in it:
class Monad m => MonadState s m | m -> s where
we cannot have a single Monad be both MonadState Int and MonadState Bool. But here is where things get a little strange.
If I add a type annotation the code will compile
test ::
( MonadState Int m
, MonadState Bool m
)
=> m ()
test = do
(((+1) :: Int -> Int) <$> get) >>= put
(not <$> get) >>= put
To me this seems very strange. A moment ago it was complaining about a very real functional dependency conflict between the two. I don't see how disambiguating the type of (+1) makes that conflict go away.
What is happening here? How does the second one compile while the first fails? And why does the first compile on 8.2.2?
Try this:
plus1 :: Int -> Int
plus1 = (+ 1)
test :: (MonadState Int m, MonadState Bool m) => m ()
test = do
(plus1 <$> get) >>= put
(not <$> get) >>= put
Compiles fine, even without the inline type annotation.
What the functor?!
The thing is, when the compiler complains in your first example, it doesn't complain about the type signature just because it decided to verify it for the heck of it. Look a bit further in the error message: ...In the second argument of ‘(<$>)’, namely ‘get’...
Aha! The source of trouble is actually get! But why?
The trouble is the bloody overloaded arithmetic. You see, operator (+) has a polymorphic type, like this:
(+) :: Num a => a -> a -> a
And naked literals also have similar type:
1 :: Num a => a
So when you write (+1), it doesn't let the compiler know that you meant Int. It admits any type a as long as there is Num a.
So the compiler turns to further surroundings to get the type. But wait! Further surroundings are also generic:
get :: MonadState a m => m a
put :: MonadState a m => a -> m ()
Ok, so maybe we can get the type from the signature of test? Let's check that! Oh, no, the signature actually contains a conflict! Bail, bail, bail! That's when you get the error.
All of this doesn't happen on the second line, because not has a non-polymorphic type not :: Bool -> Bool, so the required type of get is known. And this is why either giving an inline type annotation Int -> Int or having it come from an external function plus1 helps on the first line as well.
If you do provide enough type information for the values in the body, the compiler never has to analyze the test signature. The signature specifies that there should be a MonadState Int m dictionary, and that's good enough. Whoever calls the function will have provide the dictionary, and we'll just use that.
Now, of course, when you get around to calling this function, you'll need to provide both dictionaries MonadState Int m and MonadState Bool m, and you can't get those, so you can't actually call such function. But you sure can define it.
That being said, you CAN actually have a monad with two different MonadState instances if you're willing to be sneaky enough about it.
Of course, if you try it straight up, you get a very straight up error:
data M a = M
instance MonadState Int M
instance MonadState Bool M
> Functional dependencies conflict between instance declarations:
> instance MonadState Int M -- Defined at ...
> instance MonadState Bool M -- Defined at ...
Ok, let's start small:
data M a = M
instance MonadState Int M
> Illegal instance declaration for `MonadState a M'
> The liberal coverage condition fails in class `MonadState'
> for functional dependency: `m -> s'
> Reason: lhs type `M' does not determine rhs type `a'
> Un-determined variable: a
Alright, so something in the type of M must indicate the type Int. That makes sense. Let's add it:
data M x a = M a
instance MonadState Int (M Int)
Ok, this works. So far so good.
But of course, in order to define MonadState Bool, I need to add Bool to the type as well:
data M x y a = M a
instance MonadState Int (M Int y)
instance MonadState Bool (M x Bool)
> Functional dependencies conflict between instance declarations:
Ah, still fundep failure! Ok, well, that makes sense too.
So is there a way I can fool the compiler into not checking the instances for the fundep? Yes, there is! I can be sneaky and make the instances overlapped, like this:
instance {-# OVERLAPPABLE #-} (Num a, Show a) => MonadState a (M a y) where
get = M 42
put x = M ()
instance {-# OVERLAPPING #-} MonadState Bool (M x Bool) where
get = M True
put x = M ()
Now all that's left is the Monad instance, and we can have it all actually run:
data M x y a = M a deriving (Functor, Show)
instance Applicative (M x y) where
pure = M
(M f) <*> (M x) = M $ f x
instance Monad (M x y) where
(M x) >>= f = f x
instance {-# OVERLAPPABLE #-} (Num a, Show a) => MonadState a (M a y) where
get = M 42
put x = trace ("Setting Num: " ++ show x) $ M ()
instance {-# OVERLAPPING #-} MonadState Bool (M x Bool) where
get = M True
put x = trace ("Setting Bool: " ++ show x) $ M ()
g :: M Int Bool ()
g = test
main = print g
I've included debug trace to verify how they're actually going to work, so the above program prints:
Setting Num: 43
Setting Bool: False
M ()
The following program compiles under GHC 8.0.2 with no language extensions, and produces the expected two lines of output.
However, it does not compile if the (non-top-level) type declaration for the value write' is removed.
Also, I cannot find any (top-level) type declaration for the function write.
I find this rather odd. If this is acceptable standard Haskell, surely it should be possible to create a type declaration for the function write.
So my question is: is there such a type declaration?
import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
import Control.Monad.Writer (MonadTrans, Writer, lift, runWriter, tell, when)
import ListT (ListT, toList) -- Volkov's list-t package
logging = True
write x = when logging write' where
write' :: MonadTrans m => m (Writer [String]) ()
write' = lift $ tell [x]
f :: ListT (Writer [String]) String
f = do
write "Hello from f"
return "ABC"
g :: MaybeT (Writer [String]) Int
g = do
write "Hello from g"
return 123
main :: IO ()
main = do
print $ runWriter $ toList f
print $ runWriter $ runMaybeT g
Using GHCi (remember to put this into a separate file and load it on GHCi's command line lest you get confused by GHCi's altered typing rules):
> :t write
write :: (Applicative (m (Writer [String])), MonadTrans m) =>
String -> m (Writer [String]) ()
Why? Well,
write' :: MonadTrans m => m (Writer [String]) ()
when :: Applicative f => Bool -> f () -> f ()
when logging :: Applicative f => f () -> f ()
so, when logging write' must unify write''s m (Writer [String]) with when loggings's f, causing the combined constraint (Applicative (m (Writer [String])), MonadTrans m). But wait, let's remove the type signatures and see what the most general type is:
-- equivalent but slightly easier to talk about
write = when logging . lift . tell . (:[])
(:[]) :: a -> [a]
tell :: MonadWriter w m -> w -> m ()
lift :: (Monad m, MonadTrans t) => m a -> t m a
tell . (:[]) :: MonadWriter [a] m => a -> m ()
lift . tell . (:[]) :: (MonadWriter [a] m, MonadTrans t) => a -> t m ()
when logging . lift . tell . (:[]) = write
:: (Applicative (t m), MonadWriter [a] m, MonadTrans t) => a -> t m ()
-- GHCi agrees
Per se, there's nothing wrong with this type. However, standard Haskell does not allow this. In standard Haskell, a constraint must be of the form C v or C (v t1 t2 ...) where v is a type variable. In the compiling case, this holds: the Applicative constraint has the type variable m on the outside, and the MonadTrans is just m. This is true in the non-compiling version, too, but we also have the constraint MonadWriter ([] a) m. [] is no type variable, so the type here is rejected. This constraint arises in the compiling version, too, but the type signatures nail the variables down to produce MonadWriter [String] (Writer [String]), which is immediately satisfied and does not need to appear in the context of write.
The restriction is lifted by enabling FlexibleContexts (preferably via a {-# LANGUAGE FlexibleContexts #-} pragma, but also maybe by -XFlexibleContexts). It originally existed to prevent things such as the following:
class C a where c :: a -> a
-- no instance C Int
foo :: C Int => Int
foo = c (5 :: Int)
-- with NoFlexibleContexts: foo's definition is in error
-- with FlexibleContexts: foo is fine; all usages of foo are in error for
-- not providing C Int. This might obscure the source of the problem.
-- slightly more insiduous
data Odd a = Odd a
-- no Eq (Odd a)
oddly (Odd 0) (Odd 0) = False
oddly l r = l == r
-- oddly :: (Num a, Eq (Odd a), Eq a) => Odd a -> Odd a -> Bool
-- Now the weird type is inferred! With FlexibleContexts,
-- the weird constraint can propagate quite far, causing errors in distant
-- places. This is confusing. NoFlexibleContexts places oddly in the spotlight.
But it happens to get in the way a lot when you have MultiParamTypeClasses on.
I'm trying to pull a value out of a monad, but keep the value, which doesn't depend on the monad, polymorphic. Specifically:
foo :: (Monad mon, Ring a) => mon a
foo = return 3
main :: IO ()
main = do
x <- foo
print (x :: Double)
print (x :: Int)
The point is that the monadic portion of the computation is expensive to compute, so I only want to do it once, while keeping the value in the monad polymorphic. All of my attempts so far:
giving x the signature forall a. (Ring a) => a)
giving foo :: (Monad mon) => mon (forall a . (Ring a) => a)
enabling -XNoMonomorphismRestriction and NoMonoLocalBins
have either not worked or given errors about impredicative polymorphism, which I'm not willing to use. Is there some way to pull a polymorphic value out of a monad without impredicative polymorphism (or alternatively: is there a safe way to use impredicative polymorphism in GHC)?
You can work around the absence of impredicative polymorphism by wrapping the polymorphic value in a special data type.
newtype AnyRing = AnyRing (forall a. Ring a => a)
foo :: Monad m => m AnyRing
main = do
AnyRing x <- foo
print (x :: Double)
print (x :: Int)
I found another interesting solution, which came from this paper on typed, tagless, final interpreters (section 2.3).
The idea is to use a "duplicator":
data Dup a b = Dup a b
duplicate :: Dup a b -> (a,b)
duplicate (Dup a b) = (a,b)
instance (Num a, Num b) => Num (Dup a b) where
fromInteger x = Dup (fromInteger x) (fromInteger x)
...
foo :: (Monad mon, Num a) => mon a
foo = return 3
main :: IO ()
main = do
x <- foo
let (y,z) = duplicate x
print (y :: Double)
print (z :: Int)
This solution isn't as general as Li-yao Xia's (it only works when the monadic value is an instance of a type class), but it is interesting because you don't need higher-rank polymorphism.
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.