Why there is no `Cofunctor` typeclass in Haskell?

2020-06-08 19:10发布

问题:

Monads get fmap from Functor typeclass. Why comonads don't need a cofmap method defined in a Cofunctor class?

回答1:

Functor is defined as:

class Functor f where
    fmap :: (a -> b) -> (f a -> f b)

Cofunctor could be defined as follows:

class Cofunctor f where
    cofmap :: (b -> a) -> (f b -> f a)

So, both are technically the same, and that's why Cofunctor does not exist. "The dual concept of 'functor in general' is still 'functor in general'".

Since Functor and Cofunctor are the same, both monads and comonads are defined by using Functor. But don't let that make you think that monads and comonads are the same thing, they're not.

A monad is defined (simplifying) as:

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

whether a comonad (again, simplified) is:

class Functor w => Comonad where
    extract :: w a -> a
    extend :: (w a -> b) -> w a -> w b

Note the "symmetry".


Another thing is a contravariant functor, defined as:

import Data.Functor.Contravariant
class Contravariant f where
    contramap :: (b -> a) -> (f a -> f b)


回答2:

Actually, you're wrong: there is one!

https://hackage.haskell.org/package/acme-cofunctor



回答3:

For reference,

class Functor w => Comonad w where
  extract :: w a -> a
  duplicate :: w a -> w (w a)
  extend :: (w a -> b) -> w a -> w b

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

join :: Monad m => m (m a) -> m a

Note that given extract and extend you can produce fmap and duplicate, and that given return and >>= you can produce fmap, pure, <*>, and join. So we can focus on just pure+>>= and extract+extend.

I imagine you might be looking for something like

class InverseFunctor f where
  unmap :: (f a -> f b) -> a -> b

Since the Monad class makes it easy to "put things in" while only allowing a sort of hypothetical approach to "taking things out", and Comonad does something opposed to that, your request initially sounds sensible. However, there is a significant asymmetry between >>= and extend that will get in the way of any attempt to define unmap. Note in particular that the first argument of >>= has type m a. The second argument of extend has type w anot a.