-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #183 from twitter/jco/typeclass_bijection
Add TypeclassBijection
- Loading branch information
Showing
2 changed files
with
96 additions
and
0 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
bijection-core/src/main/scala/com/twitter/bijection/TypeclassBijection.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.twitter.bijection | ||
|
||
object TypeclassBijection { | ||
implicit class RichTypeclass[T[_], A](t: T[A]) { | ||
def bijectTo[B](implicit tcBij: TypeclassBijection[T], bij: ImplicitBijection[A, B]): T[B] = tcBij(t, bij.bijection) | ||
} | ||
|
||
object BijectionAndTypeclass { | ||
implicit def get[T[_], From, To](implicit bij: ImplicitBijection[To, From], typeclass: T[From]) = BijectionAndTypeclass(bij, typeclass) | ||
} | ||
case class BijectionAndTypeclass[T[_], From, To](bij: ImplicitBijection[To, From], typeclass: T[From]) { | ||
def apply(tc: TypeclassBijection[T]): T[To] = TypeclassBijection.typeclassBijection[T, From, To](tc, typeclass, ImplicitBijection.reverse(bij.bijection)) | ||
} | ||
|
||
def typeclassBijection[T[_], A, B](implicit tcBij: TypeclassBijection[T], typeclass: T[A], bij: ImplicitBijection[A, B]): T[B] = tcBij(typeclass, bij.bijection) | ||
def deriveFor[T[_], To](implicit tcBij: TypeclassBijection[T], batc: BijectionAndTypeclass[T, From, To] forSome { type From }): T[To] = batc(tcBij) | ||
} | ||
trait TypeclassBijection[T[_]] { | ||
def apply[A, B](tc: T[A], bij: Bijection[A, B]): T[B] | ||
} |
76 changes: 76 additions & 0 deletions
76
bijection-core/src/test/scala/com/twitter/bijection/TypeclassBijectionLaws.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package com.twitter.bijection | ||
|
||
import org.scalatest.{ PropSpec, MustMatchers } | ||
import org.scalatest.prop.PropertyChecks | ||
|
||
class TypeclassBijectionLaws extends PropSpec with PropertyChecks with MustMatchers with BaseProperties { | ||
import FakeAlgebird._ | ||
import TypeclassBijection._ | ||
|
||
case class A(x: Int, y: String) | ||
implicit val abij: Bijection[A, (Int, String)] = Bijection.build[A, (Int, String)] { A.unapply(_).get } { x => A(x._1, x._2) } | ||
|
||
implicit val orderingTypeclassBijection: TypeclassBijection[Ordering] = new TypeclassBijection[Ordering] { | ||
def apply[A, B](tc: Ordering[A], bij: Bijection[A, B]) = tc.on { bij.invert(_) } | ||
} | ||
|
||
implicit val numericTypeclassBijection: TypeclassBijection[Numeric] = new TypeclassBijection[Numeric] { | ||
def apply[A, B](tc: Numeric[A], bij: Bijection[A, B]) = | ||
new Numeric[B] { | ||
def plus(x: B, y: B) = bij(tc.plus(bij.invert(x), bij.invert(y))) | ||
def minus(x: B, y: B) = bij(tc.minus(bij.invert(x), bij.invert(y))) | ||
def times(x: B, y: B) = bij(tc.times(bij.invert(x), bij.invert(y))) | ||
def negate(x: B) = bij(tc.negate(bij.invert(x))) | ||
def fromInt(x: Int) = bij(tc.fromInt(x)) | ||
def toInt(x: B) = tc.toInt(bij.invert(x)) | ||
def toLong(x: B) = tc.toLong(bij.invert(x)) | ||
def toFloat(x: B) = tc.toFloat(bij.invert(x)) | ||
def toDouble(x: B) = tc.toDouble(bij.invert(x)) | ||
def compare(x: B, y: B) = tc.compare(bij.invert(x), bij.invert(y)) | ||
} | ||
} | ||
|
||
case class Wrapper(get: Int) | ||
implicit val wrapperbij: Bijection[Wrapper, Int] = Bijection.build[Wrapper, Int] { _.get } { Wrapper(_) } | ||
|
||
property("basic") { | ||
implicitly[Semigroup[Int]] | ||
implicitly[Semigroup[String]] | ||
implicitly[Semigroup[(Int, String)]] | ||
typeclassBijection[Semigroup, (Int, String), A] | ||
implicitly[Semigroup[(Int, String)]].bijectTo[A] | ||
deriveFor[Semigroup, A] | ||
deriveFor[Ordering, A] | ||
deriveFor[Ordering, Wrapper] | ||
deriveFor[Numeric, Wrapper] | ||
} | ||
} | ||
|
||
object FakeAlgebird { | ||
object Semigroup { | ||
implicit val intSemigroup: Semigroup[Int] = | ||
new Semigroup[Int] { | ||
override def plus(left: Int, right: Int) = left + right | ||
} | ||
|
||
implicit val strSemigroup: Semigroup[String] = | ||
new Semigroup[String] { | ||
override def plus(left: String, right: String) = left + right | ||
} | ||
|
||
implicit def tup2Semigroup[A, B](implicit s1: Semigroup[A], s2: Semigroup[B]): Semigroup[(A, B)] = | ||
new Semigroup[(A, B)] { | ||
override def plus(l: (A, B), r: (A, B)) = (s1.plus(l._1, r._1), s2.plus(l._2, r._2)) | ||
} | ||
|
||
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))) | ||
} | ||
} | ||
} | ||
trait Semigroup[T] { | ||
def plus(left: T, right: T): T | ||
} | ||
} |