Is it better to define Functor in terms of Applicative in terms of Monad, or vice versa?

前端 未结 5 545
南笙
南笙 2021-02-02 13:25

This is a general question, not tied to any one piece of code.

Say you have a type T a that can be given an instance of Monad. Since every mona

5条回答
  •  萌比男神i
    2021-02-02 14:16

    I tend to write and see written the Functor instance first. Doubly so because if you use the LANGUAGE DeriveFunctor pragma then data Foo a = Foo a deriving ( Functor ) works most of the time.

    The tricky bits are around agreement of instances when your Applicative can be more general than your Monad. For instance, here's an Err data type

    data Err e a = Err [e] | Ok a deriving ( Functor )
    
    instance Applicative (Err e) where
      pure = Ok
      Err es <*> Err es' = Err (es ++ es')
      Err es <*> _       = Err es
      _      <*> Err es  = Err es
      Ok  f  <*> Ok  x   = Ok (f x)
    
    instance Monad (Err e) where
      return = pure
      Err es >>= _ = Err es
      Ok  a  >>= f = f a
    

    Above I defined the instances in Functor-to-Monad order and, taken in isolation, each instance is correct. Unfortunately, the Applicative and Monad instances do not align: ap and (<*>) are observably different as are (>>) and (*>).

    Err "hi" <*>  Err "bye" == Err "hibye"
    Err "hi" `ap` Err "bye" == Err "hi"
    

    For sensibility purposes, especially once the Applicative/Monad Proposal is in everyone's hands, these should align. If you defined instance Applicative (Err e) where { pure = return; (<*>) = ap } then they will align.

    But then, finally, you may be capable of carefully teasing apart the differences in Applicative and Monad so that they behave differently in benign ways---such as having a lazier or more efficient Applicative instance. This actually occurs fairly frequently and I feel the jury is still a little bit out on what "benign" means and under what kinds of "observation" should your instances align. Perhaps some of the most gregarious use of this is in the Haxl project at Facebook where the Applicative instance is more parallelized than the Monad instance, and thus is far more efficient at the cost of some fairly severe "unobserved" side effects.

    In any case, if they differ, document it.

提交回复
热议问题