twitter/bijection — pull request #182 by jcoveney (2014-09-17 14:48:23+00:00)
Title: Bijection + Typeclass = Bijected Typeclass

This builds off of https://github.com/twitter/bijection/pull/179

So, the idea is this... we have a lot of case classes, and with #179, we can auto-generate bijections to and from tuples with those case classes. Next, we have a lot of type classes, and it would be nice to auto-derive THOSE... now, one thing that we could do would be to have a macro per typeclass we care about (Ordering, Monoid, and on and on). Or we can try and nail it all in one fell swoop. This is an attempt at that.

Here is an example of usage:

```
import com.twitter.bijection._
import com.twitter.bijection.macros.TypeclassBijection
import com.twitter.bijection.macros.common.Derived
import TypeclassBijection._
import Derived._

trait Semigroup[T] {
def plus(left: T, right: T): T
}

implicit val intSemigroup: Semigroup[Int] = new Semigroup[Int] {
override def plus(left: Int, right: Int) = left + right
}

case class Wrapper(get: Int) extends AnyVal

implicit val intWrapperBij: Bijection[Int, Wrapper] = Bijection.build[Int, Wrapper] { Wrapper(_) } { _.get }

implicit val semigroupTypeclassBijection: TypeclassBijection[Semigroup] = new TypeclassBijection[Semigroup] {
def apply[A, B](tc: Semigroup[A], bij: Bijection[A, B]) =
new Semigroup[B] {
override def plus(left: B, right: B) = bij(tc.plus(bij.invert(left), bij.invert(right)))
}
}

derive[Semigroup[Wrapper]]
```

This introduces a new construct: derive. Derive is meant to be a superset of implicitly, in that it will always work if implicitly works, but introduces a new type. The purpose of this new type is to have better control over the implicit graph... this is necessary because as we get more generic, creating diverging implicits is easier and easier. In this case, I couldn't figure out a good way to get around the following implicit divering:

```
implicit def typeclassBijection[T[_], A, B](implicit tcBij: TypeclassBijection[T], typeclass: T[A], bij: ImplicitBijection[A, B]): Derived[T[B]] = Derived(tcBij(typeclass, bij.bijection))
```

If we can make it not diverge that would be awesome. @travisbrown is there perhaps a way to make a macro that does the exact same lookup you would expect, except it "hides" a function (in this case typeclassBijection itself)? The problem, I think, is that typeclassBijection can fulfill both tcBij and typeclass (since they are both ```T[_]```), and so it diverges... unless it returns type Derived, which breaks the cycle. I'd love to get rid of that but if it can't be gotten rid of, I think the derive method is a fine way to say "we are going to use macros to give me something good, and the type checker can't do much more than that."

Open to thoughts, though, especially on ways to make it so that the following won't diverge:
```
implicit def typeclassBijection[T[_], A, B](implicit tcBij: TypeclassBijection[T], typeclass: T[A], bij: ImplicitBijection[A, B]):T[B] = tcBij(typeclass, bij.bijection)
```