Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply, Applicative, Monad for contravariant functor? #2324

Closed
davidrpugh opened this issue Jul 12, 2018 · 8 comments
Closed

Apply, Applicative, Monad for contravariant functor? #2324

davidrpugh opened this issue Jul 12, 2018 · 8 comments

Comments

@davidrpugh
Copy link

davidrpugh commented Jul 12, 2018

I have the following trait...

import cats._
import cats.implicits._

trait Preference[-A] {
  self =>

  def compare(a1: A, a2: A): Int

  final def ordering[A1 <: A]: Ordering[A1] = {
    new Ordering[A1] {
      def compare(a1: A1, a2: A1): Int = {
        self.compare(a1, a2)
      }
    }
  }

}


object Preference {
  
  implicit val contravariant: Contravariant[Preference] = {
    new Contravariant[Preference] {
      def contramap[A, B](fa: Preference[A])(f: B => A): Preference[B] = {
        new Preference[B] {
          def compare(b1: B, b2: B): Int = {
            fa.compare(f(b1), f(b2))
          }
        }
      }
    }
  }
}

I am learning FP and would like to explore definitions of Apply, Applicative, and possibly even Monad instances for this trait but all of these type classes are extensions of Functor. Do versions of these type classes exist in Cats for contravariant functors? If these type classes have not yet been implemented, I would be happy to contribute them!

@diesalbla
Copy link
Contributor

@drbild The contravariant counterpart of Applicative is called a Divisible. A Haskell documentation can be seen here

A covariant functor carries the intuition of being like a function that produces a type of outputs.
A contravariant functor, instead, carries the intuition of being like a function that consumes an input A. Thus, if an applicative covariant functor would join two computations F[A] and F[B] into a tuple-computation F[(A,B)], a divisible contravariant functor would be able to split the input (A,B) between a computation that consumes the A and one that consumes the B.

@LukaJCB
Copy link
Member

LukaJCB commented Jul 18, 2018

We already have these type classes, they're named ContravariantSemigroupal and ContravariantMonoidal :)

@diesalbla
Copy link
Contributor

It seems this was already done, or suggested, in #1935

@davidrpugh
Copy link
Author

@diesalbla Thanks for the link. I also posted this on SO. The answer there suggested that ContraVariantMonoidal is cats equivalent of Divisible.

I am still confused though because ContravariantMonoidal and ContravariantSemigroupal type classes define a product[A,B](fa: F[A, fb:F[B]): F[(A,B)] method which does not seem to be splitting "an input (A,B) into a computation that consumes the A and one that consumes the B."

@LukaJCB
Copy link
Member

LukaJCB commented Jul 18, 2018

product is inherited by the invariant version of Semigroupal. If you want to "an input (A,B) into a computation that consumes the A and one that consumes the B." You'll need to combine ,product with contramap. For Apply that is ap or map2 or mapN, for ContravariantSemigroupal that's contramap2 or contramapN :)

@davidrpugh
Copy link
Author

Right. So I guess I just need to read through the Haskell docs for Divisible and look through the code for ContravariantSemigroupal and ContravariantMondoidal to understand how the pieces fit together.

In sum, contravariant versions of Apply and Applicative already exist and there is no contravariant equivalent of Monad.

@diesalbla
Copy link
Contributor

Here is a discussion about the subject: https://twitter.com/mpilquist/status/431122935081078784

@LukaJCB
Copy link
Member

LukaJCB commented Jul 18, 2018

Yup Contravariant Monad doesn't really make sense. If you think about a contraFlatMap:

def contraFlatMap(fa: F[A])(f: F[B] => A): F[B]

You realize that implementing something like this is practically impossible for almost all types.
With that said, I'm going to close this issue now, unless someone has any other concerns :)

@LukaJCB LukaJCB closed this as completed Jul 18, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants