Skip to content
This repository has been archived by the owner on Nov 29, 2020. It is now read-only.

Sufficiency checker complains about a valid instance. #64

Open
sir-wabbit opened this issue Sep 21, 2018 · 3 comments
Open

Sufficiency checker complains about a valid instance. #64

sir-wabbit opened this issue Sep 21, 2018 · 3 comments
Labels
research We need to carefully think about this.

Comments

@sir-wabbit
Copy link
Contributor

sir-wabbit commented Sep 21, 2018

import scalaz.meta.minimal

trait InvariantFunctor[F[_]] {
  def imap[A, B](ma: F[A])(f: A => B)(g: B => A): F[B]
}

trait Functor[F[_]] extends InvariantFunctor[F] {
  def map[A, B](ma: F[A])(f: A => B): F[B]

  def imap[A, B](ma: F[A])(f: A => B)(g: B => A): F[B] = map(ma)(f)
}

trait Apply[F[_]] extends Functor[F] {
  def ap[A, B](fa: F[A])(f: F[A => B]): F[B]
}

@minimal(("pure", "ap"), ("unit", "zip", "map"))
trait Applicative[F[_]] extends Apply[F] {
  def unit: F[Unit] = pure(())

  def pure[A](a: A): F[A] = map(unit)(_ => a)

  def zip[A, B](fa: F[A], fb: F[B]): F[(A, B)] = ap(fa)(map(fb)(b => a => (a, b)))

  def ap[A, B](fa: F[A])(f: F[A => B]): F[B] = map(zip(f, fa)) { case (f, a) => f(a) }

  def map[A, B](ma: F[A])(f: A => B): F[B] = ap(ma)(pure(f))
}

object ComposeImpl {
  type Compose[F[_], G[_]] = { type L[X] = F[G[X]] }

  def invariant[F[_], G[_]](implicit F0: InvariantFunctor[F], G0: InvariantFunctor[G]): InvariantFunctor[Compose[F, G]#L] =
    new ComposeInvariantFunctor[F, G] {
      val F = F0
      val G = G0
    }

  def functor[F[_], G[_]](implicit F0: Functor[F], G0: Functor[G]): Functor[Compose[F, G]#L] =
    new ComposeFunctor[F, G] {
      val F = F0
      val G = G0
    }

  def apply[F[_], G[_]](implicit F0: Apply[F], G0: Apply[G]): Apply[Compose[F, G]#L] =
    new ComposeApply[F, G] {
      val F = F0
      val G = G0
    }

  def applicative[F[_], G[_]](implicit F0: Applicative[F], G0: Applicative[G]): Applicative[Compose[F, G]#L] =
    new ComposeApplicative[F, G] {
      val F = F0
      val G = G0
    }

  private trait ComposeInvariantFunctor[F[_], G[_]] extends InvariantFunctor[Compose[F, G]#L] {
    val F: InvariantFunctor[F]
    val G: InvariantFunctor[G]

    final def imap[A, B](ma: F[G[A]])(f: A => B)(g: B => A): F[G[B]] =
      F.imap[G[A], G[B]](ma)(G.imap(_)(f)(g))(G.imap(_)(g)(f))
  }

  private trait ComposeFunctor[F[_], G[_]] extends Functor[Compose[F, G]#L] {
    val F: Functor[F]
    val G: Functor[G]

    final def map[A, B](fa: F[G[A]])(f: A => B): F[G[B]] =
      F.map(fa)(G.map(_)(f))
  }

  private trait ComposeApply[F[_], G[_]] extends ComposeFunctor[F, G] with Apply[Compose[F, G]#L] {
    val F: Apply[F]
    val G: Apply[G]

    final def ap[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] =
      F.ap(fa)(F.map(f)(gab => G.ap(_)(gab)))
  }

  private trait ComposeApplicative[F[_], G[_]] extends ComposeApply[F, G] with Applicative[Compose[F, G]#L] {
    val F: Applicative[F]
    val G: Applicative[G]

    final override def pure[A](a: A): F[G[A]] = F.pure(G.pure(a))
  }
}

Sufficiency checker complains missing implementation for methods: ap OR (unit AND zip AND map) on new ComposeApplicative[F, G].

@sir-wabbit sir-wabbit added bug Something isn't working or something might not work. blocker Fix this the first opportunity! labels Sep 21, 2018
@sir-wabbit
Copy link
Contributor Author

Ah, I think the original code had a bug, the correct order of inheritance is:

  private trait ComposeApply[F[_], G[_]] extends Apply[Compose[F, G]#L] with ComposeFunctor[F, G] {
    val F: Apply[F]
    val G: Apply[G]

    final override def ap[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] =
      F.ap(fa)(F.map(f)(gab => G.ap(_)(gab)))
  }

  private trait ComposeApplicative[F[_], G[_]] extends Applicative[Compose[F, G]#L] with ComposeApply[F, G] {
    val F: Applicative[F]
    val G: Applicative[G]

    final override def pure[A](a: A): F[G[A]] = F.pure(G.pure(a))
  }

However this results in:

i64.scala:81: error: trait ComposeApplicative inherits conflicting members:
  method map in trait Applicative of type [A, B](<param> ma: F[G[A]])(<param> f: scala.this.Function1[A,B])F[G[B]]  and
  method map in trait ComposeFunctor of type [A, B](<param> fa: F[G[A]])(<param> f: scala.this.Function1[A,B])F[G[B]]
(Note: this can be resolved by declaring an override in trait ComposeApplicative.)
  private trait ComposeApplicative[F[_], G[_]] extends Applicative[Compose[F, G]#L] with ComposeApply[F, G] {
                ^

@sir-wabbit
Copy link
Contributor Author

sir-wabbit commented Sep 21, 2018

This compiles fine:

  private trait ComposeFunctor[F[_], G[_]] extends Functor[Compose[F, G]#L] {
    val F: Functor[F]
    val G: Functor[G]

    final override def map[A, B](fa: F[G[A]])(f: A => B): F[G[B]] =
      F.map(fa)(G.map(_)(f))
  }

  private trait ComposeApply[F[_], G[_]] extends Apply[Compose[F, G]#L] with ComposeFunctor[F, G] {
    val F: Apply[F]
    val G: Apply[G]

    final override def ap[A, B](fa: F[G[A]])(f: F[G[A => B]]): F[G[B]] =
      F.ap(fa)(F.map(f)(gab => G.ap(_)(gab)))
  }

  private trait ComposeApplicative[F[_], G[_]] extends Applicative[Compose[F, G]#L] with ComposeApply[F, G] {
    val F: Applicative[F]
    val G: Applicative[G]

    final override def pure[A](a: A): F[G[A]] = F.pure(G.pure(a))
  }

Turns out there is a difference between final def and final override def that affects child classes.

@sir-wabbit sir-wabbit removed blocker Fix this the first opportunity! bug Something isn't working or something might not work. labels Sep 22, 2018
@sir-wabbit
Copy link
Contributor Author

So at the end of the (second) day, I am no longer sure it's a bug. I am gonna leave it open for now in case we decide to carefully think about this case (and the interaction between sufficiency and subtyping) later.

@sir-wabbit sir-wabbit added the research We need to carefully think about this. label Sep 22, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
research We need to carefully think about this.
Projects
None yet
Development

No branches or pull requests

1 participant