2

Haskell allows to define a new type around another one, thus creating a "type with context". Hence, one may differentiate (Int, Int) in cases such as data Time = Time (Int, Int) --(h:m) and data Coord = Coord (Int, Int) --(x,y). These new types will have related functions knowing that the wrapped "base" type is effectively an Int.

One step further, is to create "generic" types, by using "type parameters" in the data clause, as in the famous monad: data Maybe t = Nothing | Just t. These kind of generic types are going to be used by wide range of functions, and for addressing many different needs, namely: exception: Maybe, global state: State, input/output: IO, nondeterminism: [], environment: Reader, logger: Writer. Here arises the convenience of having two functions: return :: a -> m a for building a context around type a, and (>>=) :: m a -> (a -> m b) -> m b for automatically having a function m a -> m b based on an previous a -> m b. This is summarized by the monad class:

class Monad m where
        return :: a -> m a
        (>>=)  :: m a -> (a -> m b) -> m b

so that new generic types have to define what means return and >>= for them to be an instance of Monad.

It seems to me that this is going to happen for every generic type, so the concrete questions are:

  1. Every generic type data MyThing t = ... must be and instance of Monad?
  2. At least in general, is it convenient that Every generic type data MyThing t = ... is an instance of Monad?
  3. Is any case where a generic type data MyThing t = ... must not be an instance of Monad? (apart from trivial cases). Why?
  4. Is it interesting that even a concrete context type, data Time = Time (Int, Int), is a Monad?

Examples, corrections and edits of the above explanation are welcome and desired.

Thanks.

cibercitizen1
  • 20,944
  • 16
  • 72
  • 95
  • 2
    Ah yeah, an obligatory reference for a question like this is of course the [typeclassopedia](http://www.haskell.org/haskellwiki/Typeclassopedia), which talks in detail about the hierarchy of `Functor`, `Monad` etc.. – leftaroundabout Dec 10 '13 at 12:01
  • @Ingo and leftaroundabout: It is clear that you two are providing obvious answers where types having one parameter but holding two or more values of it doesn't fit (in an straightforward sense) the signatures of `return` and `>>=` (f.i. this requires `(a -> m b)`: a function with just one input value). But what to say about generic types with one parameter and and only one value of it? – cibercitizen1 Dec 10 '13 at 12:28
  • I'm not sure what you mean by "and only one value of it" – perhaps my answer adresses this the way you intend. As for simple `data` with one polymorphic field in it: such a type is isomorphic to the [writer monad](http://hackage.haskell.org/package/transformers-0.3.0.0/docs/Control-Monad-Trans-Writer-Lazy.html#t:Writer). – leftaroundabout Dec 10 '13 at 12:38
  • @cibercitizen1 Note that lists can have man elements, and lists are still monads, so how many values are there cannot be the distinguishig criterion. – Ingo Dec 10 '13 at 12:39
  • @leftaroundabout OK. You provide and example in you answer below. in `data Shower a = xxxx`, `a` is used just once in `xxxx` Thx. – cibercitizen1 Dec 10 '13 at 12:40
  • 2
    @cibercitizen1, @leftroundabout: Your `Pair` and `Triple` *are* monads! `Pair` is in fact isomorphic to `(->) Bool`. – Tom Ellis Dec 10 '13 at 12:53
  • 3
    http://stackoverflow.com/q/7220436/1337941 – JJJ Dec 10 '13 at 13:05
  • That about tuples not being `Monad` was indeed bogus, sorry. – leftaroundabout Dec 10 '13 at 19:31

3 Answers3

4

To give an example that's totally not a monad, consider

data Shower a = Shower (a -> String)

What this is is a bit of the opposite (actually, the dual) of a functor: a contravariant functor.

contramap :: Contravariant f => (a -> b) -> f b -> f a

contramap f (Shower q) = Shower (q . f)

Compare this to

fmap f (Identity x)  = Identity (f $ x)

A contravariant functor can not be a (nontrivial) monad, nor something analogue. To see why, you need to consider what a monad is, actually (i.e. in category theory): it's a kind of monoid of endofunctors. It has to be endo, because the crucial operation join :: m (m a) -> m a means applying the m twice leaves you in the same category. For, if you were to fmap a function A -> B over either of the sides, you would go in the same direction:

fmap f        :: m A     -> m B
fmap (fmap f) :: m (m A) -> m (m B)

(The same goes for comonads, which are also (covariant, not contravariant) functors. Here, the operation is duplicate :: w a -> w (w a), which goes the other way around but still has to stay in the same category.)

For contravariant functors, this does not work! The reason being,

contramap f             :: q B     -> q A
contramap (contramap f) :: q (q A) -> q (q B)

i.e. if you iterate the functor it keeps flipping between contravariant and covariant. It can thus not form any such thing as a monoid structure.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • 3
    I think `Shower` is indeed *not* a monad, but your reasoning is not correct. `data Proxy a = Proxy` is both a contravariant functor and a monad. – Tom Ellis Dec 10 '13 at 12:58
  • @TomEllis: But Proxy is also a Covariant functor, isn't it? – bennofs Dec 10 '13 at 13:12
  • 1
    Sure, but leftaroundabout's argument goes "A contravariant functor can never be any such thing as a monad". That's not true. – Tom Ellis Dec 10 '13 at 13:14
  • I think leftaroundabout's argument isn't watertight, but in principle he is right that contravariant functors will typically not be monads. – Tom Ellis Dec 10 '13 at 19:21
  • @cibercitizen1: technically yes, `Proxy` is indeed a counterexample. Practically, a type constructor that's both `Functor` and `Contravariant` is just monomorphic data with a (superfluent) type argument, since the polymorphic field can not be used for anything (you could with [`fmap absurd . contramap absurd`](http://hackage.haskell.org/package/void-0.6.1/docs/Data-Void.html#absurd) create a "content barrier"). The `Monad` instance would essentially just be a [`Monoid`](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-Monoid.html) instance. – leftaroundabout Dec 10 '13 at 19:24
  • @TomEllis: actually I suspect it might be provable by the monad laws that `Contravariant` and `Functor` together reduce the capabilities to a plain `Monoid`. — My argument was actually more aimed at "contravariant functors do not give rise to something _analogue_ to what monads are as covariant functors", simply assuming it makes no sense to have a type that's _both_ covariant and contravariant. – leftaroundabout Dec 10 '13 at 19:29
  • @leftaroundabout I'd definitely think so. First, using fmap and contramap you can write `(Contravariant f, Functor f) => f a -> f Void` proving that it contains no values, and then you can reason like I did in [`Monad with no wrapped value`](http://stackoverflow.com/questions/19884548/monad-with-no-wrapped-value/19886123#19886123) to show it's probably just a monoid. – J. Abrahamson Dec 10 '13 at 19:57
3

Here's something that's not a Monad---it's not even a Functor.

-- from Data.Monoid
newtype Endo a = Endo { appEndo :: a -> a }

In particular, Endo's type parameter a shows up in both contravariant and covariant positions---this type cannot be a Functor or Contravariant functor---it would need to be both.

Of course, if you just generalize it slightly you get the Reader monad

newtype Reader r a = Reader { runReader :: r -> a }

since we've now separated out the uses of the type parameter such that one is covariant and one is contravariant.


Having shown that Endo can't be a Functor or Contravariant because it would have to be both, are there any data types which are both? There's an easy trick argument that shows that (1) there are and (2) they're always using phantom parameters.

We'll use a nullary data type (the void package provides one, but it's easy to re-implement). The interesting thing about nullary data types is that you can use one to produce absurd, the function that takes an impossible argument and returns whatever.

data Void = Void Void     -- there are no values of Void 
                          -- ... unless there are values of Void

absurd :: Void -> a
absurd (Void v) = absurd v

Which then, combined with Functor and Contravariant gives us a very interesting function

contramap absurd :: Contravariant f => f a    -> f Void
fmap      absurd :: Functor       f => f Void -> f a

fmap absurd . contramap absurd 
  :: (Contravariant f, Functor f) => f a -> f b

In other words, it lets us write a kind of functor-based coerce, which is impossible if f actually contained any values of type a, thus we know that such an f must use a as a phantom parameter.

data Constant b a = Constant { runConstant :: b }

coerceConstant :: Constant a -> Constant b
coerceConstant = Constant . runConstant

instance Functor (Constant b) where
  fmap _ = coerceConstant

instance Contravariant (Constant b) where
  contramap _ = coerceConstant

which even gives us a way to implement a very boring Monad

instance Monoid b => Monad (Constant b) where
  return _ = Constant mempty
  c >>= _ = coerceConstant c
J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
1

The concept of parameterized types is simply more general than the special case of types with just one paramter that happen to behave like monads.

So, no, not every type with kind (*->*) must be a monad and it is also not convenient.

An example:

data HomogenousTriple a = T a a a

Why and how should this be a Monad?

Ingo
  • 36,037
  • 5
  • 53
  • 100
  • Just for types with one parameter: would you provide a link or an example of it (a type not being a monad)? Thx. – cibercitizen1 Dec 10 '13 at 11:34
  • 4
    Actually this *is* a monad. It's isomorphic to a Reader monad on a type with three elements! – Tom Ellis Dec 10 '13 at 12:50
  • If Ingo is claiming that `data HomogenousTriple a = T a a a` cannot be given a monad instance then that claim is wrong. – Tom Ellis Dec 10 '13 at 19:21
  • 2
    @TomEllis I am not claiming that - I claim that a) one usually would not need it and b) not even for convenience. Or, to repeat myself: For what reason should one care to make a Monad instance for this? – Ingo Dec 10 '13 at 20:03
  • 1
    `bind` for `HomogeneousTriple` corresponds to componentwise operations. `do x <- T 1 2 3; y <- T 10 20 30; return x + y` returns `T 11 22 33` – winitzki Apr 10 '18 at 22:04
  • I see, @winitzki. I admit that others have given more convincing counterexamples. – Ingo Apr 16 '18 at 19:48