What is a contravariant functor?

前端 未结 4 1690
半阙折子戏
半阙折子戏 2020-12-23 11:56

The type blows my mind:

class Contravariant (f :: * -> *) where
  contramap :: (a -> b) -> f b -> f a

Then I read this, but con

4条回答
  •  有刺的猬
    2020-12-23 12:18

    First of all @haoformayor's answer is excellent so consider this more an addendum than a full answer.

    Definition

    One way I like to think about Functor (co/contravariant) is in terms of diagrams. The definition is reflected in the following ones. (I am abbreviating contramap with cmap)

          covariant                           contravariant
    f a ─── fmap φ ───▶ f b             g a ◀─── cmap φ ─── g b
     ▲                   ▲               ▲                   ▲
     │                   │               │                   │
     │                   │               │                   │
     a ────── φ ───────▶ b               a ─────── φ ──────▶ b
    

    Note: that the only change in those two definition is the arrow on top, (well and the names so I can refer to them as different things).

    Example

    The example I always have in head when speaking about those is functions - and then an example of f would be type F a = forall r. r -> a (which means the first argument is arbitrary but fixed r), or in other words all functions with a common input. As always the instance for (covariant) Functor is just fmap ψ φ = ψ . φ`.

    Where the (contravariant) Functor is all functions with a common result - type G a = forall r. a -> r here the Contravariant instance would be cmap ψ φ = φ . ψ.

    But what the hell does this mean

    φ :: a -> b and ψ :: b -> c

    usually therefore (ψ . φ) x = ψ (φ x) or x ↦ y = φ x and y ↦ ψ y makes sense, what is ommited in the statement for cmap is that here

    φ :: a -> b but ψ :: c -> a

    so ψ cannot take the result of φ but it can transform its arguments to something φ can use - therefore x ↦ y = ψ x and y ↦ φ y is the only correct choice.

    This is reflected in the following diagrams, but here we have abstracted over the example of functions with common source/target - to something that has the property of being covariant/contravariant, which is a thing you often see in mathematics and/or haskell.

                     covariant
    f a ─── fmap φ ───▶ f b ─── fmap ψ ───▶ f c
     ▲                   ▲                   ▲
     │                   │                   │
     │                   │                   │
     a ─────── φ ──────▶ b ─────── ψ ──────▶ c
    
    
                   contravariant
    g a ◀─── cmap φ ─── g b ◀─── cmap ψ ─── g c
     ▲                   ▲                   ▲
     │                   │                   │
     │                   │                   │
     a ─────── φ ──────▶ b ─────── ψ ──────▶ c
    

    Remark:

    In mathematics you usually require a law to call something functor.

            covariant
       a                        f a
      │  ╲                     │    ╲
    φ │   ╲ ψ.φ   ══▷   fmap φ │     ╲ fmap (ψ.φ)
      ▼    ◀                   ▼      ◀  
      b ──▶ c                f b ────▶ f c
        ψ                       fmap ψ
    
           contravariant
       a                        f a
      │  ╲                     ▲    ▶
    φ │   ╲ ψ.φ   ══▷   cmap φ │     ╲ cmap (ψ.φ)
      ▼    ◀                   │      ╲  
      b ──▶ c                f b ◀─── f c
        ψ                       cmap ψ
    

    which is equivalent to saying

    fmap ψ . fmap φ = fmap (ψ.φ)
    

    whereas

    cmap φ . cmap ψ = cmap (ψ.φ)
    

提交回复
热议问题