Looking at MaybeT:
λ: import Monad.Trans
λ: import Monad.Trans.Maybe
λ: :t MaybeT
MaybeT :: m (Maybe a) -> MaybeT m a
In MaybeT's signature, can m be any higher-kinded type, i.e. * -> *?
I'm trying to learn Monad Transformers, so I was curious why the m did not have a constraint of Monad.
To quote Learn You A Haskell:
Another example of a parameterized type that we've already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord type class. If we were defining a mapping type, we could add a type class constraint in the data declaration:
data (Ord k) => Map k v = ...
However, it is a very strong convention in Haskell to never add type class constraints in data declarations. Why? Well, because we don't benefit a lot, but we end up writing more class constraints, even when we don't need them. If we put or don't put the Ord k constraint in the data declaration for Map k v, we're going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don't put the constraint in the data declaration, we don't have to put (Ord k) => in the type declarations of functions that don't care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. It's type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: Ord k => Map k a -> [(k, a)], even though the function doesn't do any comparing of keys by order.
So don't put type constraints into data declarations even if it seems to make sense, because you'll have to put them into the function type declarations either way.
I hope that answers your question. Although the m in MaybeT m a is unconstrained yet there is an implicit assumption that m is an instance of Monad. In fact, if m is not an instance of Monad then you wouldn't be able to do much with a MaybeT m a value because most functions would require that m be an instance of Monad.
In MaybeT's signature, can m be any higher-kinded type, i.e. * -> *?
No:
> :k MaybeT
MaybeT :: (* -> *) -> * -> *
So, the m in the type MaybeT m a must be exactly of kind * -> *, and not of any other kind.
For instance, note the kind error below:
> :k MaybeT (State Int)
MaybeT (State Int) :: * -> *
> :k MaybeT State
Expecting one more argument to ‘State’
The first argument of ‘MaybeT’ should have kind ‘* -> *’,
but ‘State’ has kind ‘* -> * -> *’
In a type in a GHCi command: MaybeT (State)
can m be any higher-kinded type, i.e. * -> *?
It can be anything with kind * -> *. Other than that, there are no restrictions in principle.
why the m did not have a constraint of Monad.
The constraint is provided through the types of the functions that use MaybeT, rather than through the MaybeT constructor. Adding constraints to constructors is not useful unless GADTs are involved (I believe transformers does not use GADTs for the sake of simplicity and compliance to the standard). See this question and its answers for extra commentary on constraints and datatype declarations.
Related
When type variables are constrained by classes in Haskell, I understand what that means.
For example, in a function declaration,
myFunction :: Foldable f => f a -> b
means that f is a type with an instance of Foldable which wraps some other type a.
But what does it mean when a type variable is constrained by a kind?
Consider for instance this definition for Foldable:
class Foldable (t :: * -> *) where
Also, does the fact that 1 example is from a function definition and the other example is from a class definition make any difference in what the constraint means?
t :: * -> * is not a constraint, it is a kind annotation. In this case, it is used to remark that Foldable can take as arguments type constructors such as Maybe, Identity, [], or even partially applied ones like Either Bool and (,) String. By contrast Foldable Int and Foldable [Bool] would be kind errors.
t :: * -> * can be read as "t maps types to types".
The point is, when we have Foldable f we then use f as in f a, applying f to one argument. If we allow f = Maybe we get Maybe a which makes sense. If we allowed f = Int, we would get Int a which is meaningless.
When having a parametrized type:
data A a=X a| Y
I have tried (successfully) implementing Functor and Applicative without specifiying the type parameter:
instance Functor A where instead of instance Functor (A a) where.Why does it work ? Looking in LYAH it seems all examples specify the type parameter in all their typeclass instances.
When should you neglect the type parameter ?
instance Functor A where instead of instance Functor (A a) where.
Why does it work ?
I find this easier to understand using GHC's kinding system. Let's start from a simple case, and experiment in GHCi:
> :k Eq Int
Eq Int :: Constraint
This tells us that Eq Int is a constraint, some property that might be verified during type checking. Indeed, if we type check (12 :: Int) == (42 :: Int), the compiler will verify that integers can be compared, resolving the constraint Eq Int.
What is Eq alone, the name of the class without the Int parameter?
> :k Eq
Eq :: * -> Constraint
This tells us that Eq can be thought of a function from types (* is the kind of types) to constraint.
Indeed, in Eq Int, Int is a type, so we have Int :: * making Int a well-kinded argument to pass to Eq.
Enough of type classes, what about type constructors?
> :k Maybe Int
Maybe Int :: *
No surprise, Maybe Int is a type
> :k Maybe
Maybe :: * -> *
Maybe instead, is a function from types to types (*->*). This is indeed what the Maybe type constructor does: mapping a type (Int) to a type (Maybe Int).
Back to the original question. Why can't we write instance Functor (A a) but we can instead write instance Functor A? Well, we have that
> :k A Int
A Int :: *
> :k A
A :: * -> *
and, most importantly,
> :k Functor
Functor :: (* -> *) -> Constraint
This tells us the the kind of the Functor type class is not the same kind of the Eq type class. Eq expects a type as an argument, while Functor expects something of kind (* -> *) as an argument. A fits that kind, while A Int does not.
This happens when, in the definition of the class, the argument is applied to some other type. E.g.
class C1 a where
foo :: a -> Bool
results in C1 :: * -> Constraint. Instead,
class C2 f where
bar :: f Int -> Bool
results in C2 :: (* -> *) -> Constraint, since f is not itself used as a type, f Int is, so f must be a parameterized type of kind * -> *.
When should you neglect the type parameter?
The type parameter is not "neglected". Let us first take a look at the Functor type class:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Notice the f a and f b in the type signature. We thus here "construct types" for the type signature of fmap.
Here f is thus basically a function that takes a type and transforms it into a type. For example if f ~ A if a ~ Int, then f a generates the A Int type.
Learn You a Haskell for the Greater Good! actually explains this in its chapter about Functors, Applicatives and Monoids:
Many times, we want to make our types instances of certain type
classes, but the type parameters just don't match up for what we want
to do. It's easy to make Maybe an instance of Functor, because
the Functor type class is defined like this:
class Functor f where
fmap :: (a -> b) -> f a -> f b
So we just start out with:
instance Functor Maybe where
And then implement fmap. All the type parameters add up because the Maybe takes the place of f in
the definition of the Functor type class and so if we look at fmap
like it only worked on Maybe, it ends up behaving like:
fmap :: (a -> b) -> Maybe a -> Maybe b
(...)
If I inspect the kind of Maybe I get this:
λ> :k Maybe
Maybe :: * -> *
Now, if I inspect the kind of Monad I get this:
λ> :k Monad
Monad :: (* -> *) -> Constraint
What is Constraint there and why it is needed ? Why not just this * -> * ?
Unlike Maybe, Monad is not a type; it is a typeclass.
The same goes for other typeclasses:
Num :: * -> Constraint
Functor :: (* -> *) -> Constraint
Bifunctor :: (* -> * -> *) -> Constraint
Where * represents concrete types (such as Bool or Int), -> represent higher-kinded types (such as Maybe), and Constraint represents the idea of a type constraint. This is why:
As we know we can't make a signature like this:
return :: a -> Monad a -- This is nonsense!
Because Monad should be used as a constraint, to say that, 'this must be a monad to work':
return :: (Monad m) => a -> m a
We do this because we know that return can't work on any old type m, so we define the behaviour of return for different types under the name Monad. In other words, there is no single thing that can be called a Monad, but only behaviour that can be called Monadic.
For this reason, we have created this type constraint, saying that we must have pre-defined something as a Monad to use this function. This is why the kind of Monad is (* -> *) -> Constraint - it itself is not a type!
Maybe is an instance of Monad. This means that somewhere, someone has written:
instance Monad Maybe where
(>>=) = ... -- etc
...and defined how Maybe should behave as a Monad. This is why we can use Maybe with functions or types that have the prefix constraint Monad m => .... This is essentially where one defines the constraint applied by Monad.
Constraint is the kind of e.g. Show Int, Monad Maybe, and Monoid [a]. Roughly, it is the kind of everything that can occur on the left side of => in type annotations.
Now since
Show Int :: Constraint
and Int is a type, i.e.
Int :: *
we can assign a functional kind to Show as follows
Show :: * -> Constraint
^-- the result kind
^-- the kind of Int
In your case it just happens that a Monad takes argument like Maybe, so
Maybe Int :: *
Maybe :: * -> *
Monad :: (* -> *) -> Constraint
^-- the result kind
^-- the kind of Maybe
I don't have quite the vocabulary for phrasing this question (and therefore for searching for answers, so apologies if the answer is easily available). Consider the following
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (RunFoo n) => n a -> m a
newtype RunFast a = RunFast a
newtype RunSlow a = RunSlow a
fooExample :: (RunFoo m) => m Bool
fooExample = undefined
fooWrapperExample :: (RunFooWrapper m) => m Bool
fooWrapperExample = doRunFoo fooExample
This does not compile: Could not deduce (RunFoo n0) arising from a use of ‘doRunFoo’.
It seems that the compiler (GHC 7.10) is insisting on a concrete instantiation of the m from fooExample, and is therefore refusing to continue. But in this case I can't see why the program is ill-typed - fooExample explicitly defines a RunFoo m and all doRunFoo requires is a RunFoo x. So why doesn't this work?
As a supplementary question, is there some kind of special extension (something to do with existential types perhaps) that would allow a program like this? Ideally I'd like to be able to say that doRunFoo takes anything defined existentially (?) as RunFoo m => m (rather than taking any concrete instantiation of RunFoo).
Motivation
I'd like to create composable functions that operate on some aspect of a type constrained by a typeclass - I can provide a concrete example of what I mean if necessary!
Edit with more motivation and alternative implementation
I was curious about the answer to this question in the general case, but I thought I'd add that in the context I ran across this issue what I really wanted is for a sort of constrained delegation between monads. So I wanted to write functions in a monad constrained only by a typeclass that invoked monads in another type class. The top level function could then be run in different contexts, performing the same logic but with the underlying implementation swapped out according to the wrapping monad. As there was a one-to-one correspondence between the wrapping and the implementation monad I was able to use type families to do this, so
class (RunFoo (RunFooM m)) => RunFooWrapper m where
type RunFooM m :: * -> *
doRunFoo :: RunFooM m a -> m a
instance RunFooWrapper RunFooWrapperSlow where
type RunFooM RunFooWrapperSlow = RunSlow
doRunFoo :: [...]
This meant that the resolution of the fooExample m was determined by the class context of the wrapper monad, and seems to work fine, but it is a very narrow solution compared to that provided by haoformayor.
RankNTypes
{-# language RankNTypes #-}
class RunFoo m where
runFoo :: m a -> a
class RunFooWrapper m where
doRunFoo :: (forall n. RunFoo n => n a) -> m a
fooExample :: RunFoo m => m Bool
fooExample = undefined
fooWrapperExample :: RunFooWrapper m => m Bool
fooWrapperExample = doRunFoo fooExample
The (forall n. RunFoo n => n a) -> m a is the key difference. It lets you pass in fooExample, which has the type forall m. RunFoo m => m Bool (the forall being implicitly added by the compiler) and so the m unifies with the n and everybody is happy. Though I cannot read minds, I believe this type reflects your true intent. You only wanted a RunFoo instance, nothing more, and having the n scoped to the first argument provides it.
The problem was that your given code is implicitly typed as forall n. RunFoo n => n a -> m a. That means you need to first pick an n such that RunFoo n and then come up with a value with type n a to pass in as the first argument. This simple act of moving the parentheses (increasing the rank of n) changes the meaning entirely.
For an example of someone who had the same problem, see this Stack Overflow question. For a better explanation of RankNTypes than I could provide, see Oliver's blog post on it.
Looking at the source for Monad:
class Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
{-# INLINE (>>) #-}
m >> k = m >>= \_ -> k -- <-- !! right here !!
fail s = error s
You can see that >> has a default implementation. My question is, is it considered good or bad practice, and why, to include a function/combinator in the typeclass, instead of providing it separately outside of the typeclass?
That is, why not:
class Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
return :: a -> m a
fail :: String -> m a
fail s = error s
and somewhere else:
(>>) :: forall a b. m a -> m b -> m b
{-# INLINE (>>) #-}
m >> k = m >>= \_ -> k
As far as I know, there are two main reasons to include "extra" functions:
Efficiency: Sometimes an inefficient generic implementation exists, and the class's author expects instance-specific implementations to be significantly better. In such cases, including the function in the class with a default implementation means that instances can use optimized versions if they want, but aren't required to. For a fun example of this, look at Foldable. This is also true of Monad.
Choice of implementation: Often there are several subsets of the class functions that could be used; including all the potential functions and using default implementations in terms of each other means that an instance can pick some functions to implement and get the rest automatically. This also applies to Foldable, but Eq is a simpler example.
This way, custom >> can be implemented for monads where it can be done more efficiently or naturally than via m >>= \_ -> k, but a default implementation still exists.
Another argument for including methods in the typeclass is when they should satisfy certain laws, or when they make the statement of those laws clearer. I would argue that the laws ought morally to be associated with the typeclass ("what must I provide in order to declare an instance of this class?") For example, you might prefer to state the monad laws in terms of return, join and fmap, rather than return and >>=; that encourages you to put all four operators in the type class (and make Monad a subclass of Functor!), and give default definitions of >>= in terms of join and vice versa.`