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.
Related
I'm trying to learn monads better and am playing around with it in Haskell. I defined a monad in this way:
module TESTMonad where
import Control.Monad
newtype TEST i = TEST {getTEST :: ((i, Int), Int)} deriving (Show, Eq, Ord)
instance Functor TEST where
fmap f (TEST ((x,y), z)) = TEST ((f x, y), z)
instance Applicative TEST where
pure = return
tf <*> tx = tf >>= \f -> tx >>= \x -> return (f x)
instance Monad TEST where
return x = TEST ((x, 1), 1)
(TEST ((x, y), z)) >>= f = TEST ((plusOne a, b), c)
where
((a, b), c) = getTEST (f x)
plusOne :: Int -> Int
plusOne x = x+1
but I get the following error when I'm trying to compile it:
TESTMonad.hs:16:47: error:
• Couldn't match expected type ‘Int’ with actual type ‘b’
‘b’ is a rigid type variable bound by
the type signature for:
(>>=) :: forall a b. TEST a -> (a -> TEST b) -> TEST b
at TESTMonad.hs:16:24
• In the first argument of ‘plusOne’, namely ‘a’
In the expression: plusOne a
In the expression: (plusOne a, b)
• Relevant bindings include
a :: b (bound at TESTMonad.hs:18:19)
f :: a -> TEST b (bound at TESTMonad.hs:16:28)
(>>=) :: TEST a -> (a -> TEST b) -> TEST b
(bound at TESTMonad.hs:16:5)
Failed, modules loaded: none.
I clearly know that I might be doing lots of things in the wrong way but I have no idea what are they. Any comment would be appreciated. Thank you in advance!
The Monad instance cannot be constrained. The type of (>>=) must be
Monad m => m a -> (a -> m b) -> m b
but your definition, using plusOne :: Int -> Int, makes the type
Monad m => m Int -> (Int -> m Int) -> m Int
You could safely apply plusOne to either of the other values wrapped inside TEST, as they are already defined to be Ints.
The definition of (>>=) has no idea what the type of x might be, and the caller gets to choose f, so it has no idea what the type of f x might either. As a result, you can't really do anything with it except use it as-is.
data M a = M a deriving (Show)
unitM a = M a
bindM (M a) f = f a
joinM :: M (M a) -> M a
joinM m = m `bindM` id
joinM' :: M a -> a
joinM' m = m `bindM` id
Note that joinM (M 0) will fail to type check, whereas joinM' (M 0) will be fine.
My question: why is joinM defined as M (M a) -> M a but not as M a -> a?
From my understanding,
unitM puts the value a into the monad M a
joinM gets the value a from the monad M a
So joinM should really work on any monad, i.e., not necessarily nested ones such as M (M a), right?
The point of monads is that you can't get a value out of them. If join had type m a -> a then the IO monad would be perfectly useless, since you could just extract the values freely. The point of monads is that you can chain computations together (>>= can be defined in terms of join, provided you have return and fmap) and put values into a monadic context, but you can't (in general) get them out.
In your specific case, you've defined what is essentially the identity monad. In that case, it's easy to extract the value; you just strip away the layer of M and move on with your life. But that's not true for general monads, so we restrict the type of join so that more things can be monads.
Your bindM is not of the correct type, by the way. The general type of >>= is
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Your function has type
bindM :: M a -> (a -> b) -> b
Notice that your type is more general. Hence, again, in your specific case, you can get away with being looser on the requirements of joinM, whereas specific monads cannot. Try giving bindM an explicit type signature of M a -> (a -> M b) -> M b and then see if both of your join functions still typecheck.
Given a type constructor M :: * -> *, and a type a, consider the following sequence of types
a, M a, M (M a), M (M (M a)), ...
If we have polymorphic functions return :: b -> M b and extract :: M b -> b (your alternative join), we can convert a value of any type above to any other type above. Indeed, we can add and remove M as wanted using these two functions, choosing the type b suitably. In more casual words, we can move both to the right and to the left in such type sequence.
In a monad, instead, we can move to the right without limits (using return). We can also move to the left almost everywhere: the important exception being that we can not move from M a to a. This is realized by join :: M (M c) -> M c, which has the type of extract :: M b -> b restricted to the case b = M c. So essentially, we can move left (as with extract), but only when we end up in a type which has at least one M -- hence, no further to the left than M a.
As Carl mentions above in the comments this restriction makes it possible to have more monads. For instance, if M = [] is the list monad, we can properly implement return and join but not extract.
return :: a -> [a]
return x = [x]
join :: [[a]] -> [a]
join xss = concat xss
Instead extract :: [a] -> a can not be a total function, since extract [] :: a would be well typed, yet tries to extract a value of type a from the empty list. It is a well-known theoretical result that no total expression can have the polymorphic type ... :: a. We can have undefined :: a, fromJust Nothing :: a, or head [] :: a but all of these are not total, and will raise an error when evaluated.
I have some Haskell code that won't compile (with GHC 8.0.2). I think I understand the basic problem, but I would like to understand it better so I can avoid this in the future.
My library looks similar to this:
{-# language TypeFamilyDependencies #-}
{-# language GADTs #-}
{-# language RankNTypes #-}
module Lib where
type Key = Int
class Handle m where
type Connection m = c | c -> m
withConnection :: Connection m -> m a -> IO a
class (Handle m) => Data m where
getKeyVal :: Key -> m String
data SomeConn where
SomeConn :: (Data m) => Connection m -> SomeConn
useConnection :: SomeConn -> (forall m. Data m => m String) -> IO String
useConnection (SomeConn c) action = withConnection c action
The idea is that Data m represents a class of monads similar to ReaderT (Connection m) IO. I am hoping to write generic functions with the methods of this typeclass, and have the exact method instance be determined by the connection type wrapped with SomeConn (which is chosen at run-time).
Now the following code
getKeyValWith :: SomeConn -> Key -> IO String
getKeyValWith c = (useConnection c). getKeyVal
gives me the following error from GHC 8.0.2:
• Couldn't match type ‘m0 String’
with ‘forall (m :: * -> *). Data m => m String’
Expected type: m0 String -> IO String
Actual type: (forall (m :: * -> *). Data m => m String)
-> IO String
• In the first argument of ‘(.)’, namely ‘useConnection c’
In the expression: useConnection c . getKeyVal
In an equation for ‘getKeyValWith’:
getKeyValWith c = useConnection c . getKeyVal
Strangely enough, the following works just fine:
getKeyValWith c k = useConnection c (getKeyVal k)
Less surprisingly, so does this:
getKeyValWith (SomeConn c) = withConnection c . getKeyVal
Is there a simple rule to understand why GHC doesn't like the first example, but the other examples are okay? Is there a way I can ask GHC for more information about what it's doing when it tries to compile the first definition? I understand this is probably not idiomatic Haskell (what some call the "Existential/typeclass anti-pattern").
Edit:
I should add that I run into the same problem even if I explicitly add the type getKeyVal :: Key -> (Data m => m String) in the first example. I can even give this function its own name with my chosen type signature (which typechecks), but I get the same error. But I see now that even when I explicitly add the type, running :t in GHCI (with -XRankNTypes) on it gives me back the original type with Data m => floated to the left. So I think I understand why GHC is balking at me. Can I force GHC to use my chosen type?
This is all about .. It's unable to pass a polymorphic argument between the functions, hence f . g doesn't work if f is rank-2 polymorphic. Notice the following works:
(~.) :: ((∀ m. Data m => m String) -> z) -> (x -> (∀ m. Data m => m String))
-> x -> z
(~.) f g x = f (g x)
getKeyValWith :: SomeConn -> Key -> IO String
getKeyValWith c = useConnection c ~. getKeyVal
Ideally, . would have a type like
(.) :: ∀ c . ((∀ y . c y => y) -> z) -> x -> ((∀ y . c y => y) -> x)
-> x -> z
and thus cover all special cases like ~. above. But this is not possible – it would require inferring the weakest possible constraint c to pick in any given situation – in the traditional case, c y = y~y₀ – and I'm pretty sure that is uncomputable in general.
(An interesting question is how far we could get if the compiler just inlined . as much as possible before type-checking, as it right now already does with $. If it did automatic eta-expansion, it could certainly get useConnection c . getKeyVal to work, but automatic eta-expansion is in general not a good idea...)
Hiding the Rank-2 polymorphism by wrapping the polymorphic argument in a GADT, as you did with SomeConn, is the usual workaround.
The type signature for >>= is the following:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
And the following makes sense to me (it is also one of the monad laws):
(>>=) (Just 1) (id . return) == Just 1
Yet the Prelude gives the following:
Prelude> :t (>>=) (Just 1) id
(>>=) (Just 1) id :: Num (Maybe b) => Maybe b
I would have expected the Prelude to return some error as the type signature on id is (a -> a) as opposed to Monad m => (a -> m b).
Is there a great way to understand what is going on here? Is there any use for (>>=) (Just 1) id?
The type of id is c -> c (using a different letter so not to conflict with a and b that occur in the type of >>=). We can unify c -> c with a -> Maybe b if we pick c = a = Maybe b.
So this means that >>= in your example is used at type:
(>>=) :: Maybe (Maybe b) -> (Maybe b -> Maybe b) -> Maybe b
Now you have
(>>=) (Just 1) id
For Just 1 to be of type Maybe (Maybe b), Maybe b must be in Num (because then 1 can be interpreted as Maybe b).
New to Haskell, and am trying to figure out this Monad thing. The monadic bind operator -- >>= -- has a very peculiar type signature:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
To simplify, let's substitute Maybe for m:
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
However, note that the definition could have been written in three different ways:
(>>=) :: Maybe a -> (Maybe a -> Maybe b) -> Maybe b
(>>=) :: Maybe a -> ( a -> Maybe b) -> Maybe b
(>>=) :: Maybe a -> ( a -> b) -> Maybe b
Of the three the one in the centre is the most asymmetric. However, I understand that the first one is kinda meaningless if we want to avoid (what LYAH calls boilerplate code). However, of the next two, I would prefer the last one. For Maybe, this would look like:
When this is defined as:
(>>=) :: Maybe a -> (a -> b) -> Maybe b
instance Monad Maybe where
Nothing >>= f = Nothing
(Just x) >>= f = return $ f x
Here, a -> b is an ordinary function. Also, I don't immediately see anything unsafe, because Nothing catches the exception before the function application, so the a -> b function will not be called unless a Just a is obtained.
So maybe there is something that isn't apparent to me which has caused the (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b definition to be preferred over the much simpler (>>=) :: Maybe a -> (a -> b) -> Maybe b definition? Is there some inherent problem associated with the (what I think is a) simpler definition?
It's much more symmetric if you think in terms the following derived function (from Control.Monad):
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
(f >=> g) x = f x >>= g
The reason this function is significant is that it obeys three useful equations:
-- Associativity
(f >=> g) >=> h = f >=> (g >=> h)
-- Left identity
return >=> f = f
-- Right identity
f >=> return = f
These are category laws and if you translate them to use (>>=) instead of (>=>), you get the three monad laws:
(m >>= g) >>= h = m >>= \x -> (g x >>= h)
return x >>= f = f x
m >>= return = m
So it's really not (>>=) that is the elegant operator but rather (>=>) is the symmetric operator you are looking for. However, the reason we usually think in terms of (>>=) is because that is what do notation desugars to.
Let us consider one of the common uses of the Maybe monad: handling errors. Say I wanted to divide two numbers safely. I could write this function:
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv n d = n `div` d
Then with the standard Maybe monad, I could do something like this:
foo :: Int -> Int -> Maybe Int
foo a b = do
c <- safeDiv 1000 b
d <- safeDiv a c -- These last two lines could be combined.
return d -- I am not doing so for clarity.
Note that at each step, safeDiv can fail, but at both steps, safeDiv takes Ints, not Maybe Ints. If >>= had this signature:
(>>=) :: Maybe a -> (a -> b) -> Maybe b
You could compose functions together, then give it either a Nothing or a Just, and either it would unwrap the Just, go through the whole pipeline, and re-wrap it in Just, or it would just pass the Nothing through essentially untouched. That might be useful, but it's not a monad. For it to be of any use, we have to be able to fail in the middle, and that's what this signature gives us:
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
By the way, something with the signature you devised does exist:
flip fmap :: Maybe a -> (a -> b) -> Maybe b
The more complicated function with a -> Maybe b is the more generic and more useful one and can be used to implement the simple one. That doesn't work the other way around.
You can build a a -> Maybe b function from a function f :: a -> b:
f' :: a -> Maybe b
f' x = Just (f x)
Or, in terms of return (which is Just for Maybe):
f' = return . f
The other way around is not necessarily possible. If you have a function g :: a -> Maybe b and want to use it with the "simple" bind, you would have to convert it into a function a -> b first. But this doesn't usually work, because g might return Nothing where the a -> b function needs to return a b value.
So generally the "simple" bind can be implemented in terms of the "complicated" one, but not the other way around. Additionally, the complicated bind is often useful and not having it would make many things impossible. So by using the more generic bind monads are applicable to more situations.
The problem with the alternative type signature for (>>=) is that it only accidently works for the Maybe monad, if you try it out with another monad (i.e. List monad) you'll see it breaks down at the type of b for the general case. The signature you provided doesn't describe a monadic bind and the monad laws can't don't hold with that definition.
import Prelude hiding (Monad, return)
-- assume monad was defined like this
class Monad m where
(>>=) :: m a -> (a -> b) -> m b
return :: a -> m a
instance Monad Maybe where
Nothing >>= f = Nothing
(Just x) >>= f = return $ f x
instance Monad [] where
m >>= f = concat (map f m)
return x = [x]
Fails with the type error:
Couldn't match type `b' with `[b]'
`b' is a rigid type variable bound by
the type signature for >>= :: [a] -> (a -> b) -> [b]
at monadfail.hs:12:3
Expected type: a -> [b]
Actual type: a -> b
In the first argument of `map', namely `f'
In the first argument of `concat', namely `(map f m)'
In the expression: concat (map f m)
The thing that makes a monad a monad is how 'join' works. Recall that join has the type:
join :: m (m a) -> m a
What 'join' does is "interpret" a monad action that returns a monad action in terms of a monad action. So, you can think of it peeling away a layer of the monad (or better yet, pulling the stuff in the inner layer out into the outer layer). This means that the 'm''s form a "stack", in the sense of a "call stack". Each 'm' represents a context, and 'join' lets us join contexts together, in order.
So, what does this have to do with bind? Recall:
(>>=) :: m a -> (a -> m b) -> m b
And now consider that for f :: a -> m b, and ma :: m a:
fmap f ma :: m (m b)
That is, the result of applying f directly to the a in ma is an (m (m b)). We can apply join to this, to get an m b. In short,
ma >>= f = join (fmap f ma)