Skip to content

Put implicit support for evidence from predef types #3508

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

Merged
merged 3 commits into from
Jul 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions core/src/main/scala-2.12/cats/evidence/AsSupport.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cats.evidence

private[evidence] trait AsSupport {

/**
* In 2.13 there is a method on ev that makes this safe.
* But lack of this method does not make the cast unsafe
* it just makes it not provable without the cast.
*/
@inline implicit def asFromPredef[A, B](implicit ev: A <:< B): A As B =
As.refl[A].asInstanceOf[A As B]
}
12 changes: 12 additions & 0 deletions core/src/main/scala-2.12/cats/evidence/IsSupport.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package cats.evidence

private[evidence] trait IsSupport {

/**
* In 2.13 there is a method on ev that makes this safe.
* But lack of this method does not make the cast unsafe
* it just makes it not provable without the cast.
*/
@inline implicit def isFromPredef[A, B](implicit ev: A =:= B): A Is B =
Is.refl[A].asInstanceOf[A Is B]
}
10 changes: 10 additions & 0 deletions core/src/main/scala-2.13+/cats/evidence/AsSupport.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package cats.evidence

private[evidence] trait AsSupport {
@inline implicit def asFromPredef[A, B](implicit ev: A <:< B): A As B = {
// we need F to be covariant, and the type lambda loses that
// if we write As[A, ?]
type F[+Z] = As[A, Z]
ev.substituteCo[F](As.refl[A])
}
}
6 changes: 6 additions & 0 deletions core/src/main/scala-2.13+/cats/evidence/IsSupport.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package cats.evidence

private[evidence] trait IsSupport {
@inline implicit def isFromPredef[A, B](implicit ev: A =:= B): A Is B =
ev.substituteCo[Is[A, *]](Is.refl[A])
}
18 changes: 13 additions & 5 deletions core/src/main/scala/cats/evidence/As.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ sealed abstract class As[-A, +B] extends Serializable {
@inline final def compose[C](that: (C As A)): (C As B) = As.compose(this, that)

@inline final def coerce(a: A): B = As.witness(this)(a)

/**
* A value `A As B` is always sufficient to produce a similar `Predef.<:<`
* value.
*/
@inline final def toPredef: A <:< B = {
type F[-Z] = <:<[Z, B]
substitute[F](implicitly[B <:< B])
}
}

sealed abstract class AsInstances {
Expand All @@ -49,7 +58,7 @@ sealed abstract class AsInstances {
}
}

object As extends AsInstances {
object As extends AsInstances with AsSupport {

/**
* In truth, "all values of `A Is B` are `refl`". `reflAny` is that
Expand Down Expand Up @@ -88,12 +97,11 @@ object As extends AsInstances {

/**
* It can be convenient to convert a <:< value into a `<~<` value.
* This is not strictly valid as while it is almost certainly true that
* `A <:< B` implies `A <~< B` it is not the case that you can create
* evidence of `A <~< B` except via a coercion. Use responsibly.
* This is not actually unsafe, but was previously labeled as such out
* of an abundance of caution
*/
def fromPredef[A, B](eq: A <:< B): A As B =
reflAny.asInstanceOf[A As B]
asFromPredef(eq)

/**
* We can lift subtyping into any covariant type constructor
Expand Down
19 changes: 13 additions & 6 deletions core/src/main/scala/cats/evidence/Is.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,16 @@ abstract class Is[A, B] extends Serializable {
* A value `A Is B` is always sufficient to produce a similar `Predef.=:=`
* value.
*/
@deprecated("Use toPredef for consistency with As", "2.2.0")
@inline final def predefEq: A =:= B =
substitute[=:=[A, *]](implicitly[A =:= A])

/**
* A value `A Is B` is always sufficient to produce a similar `Predef.=:=`
* value.
*/
@inline final def toPredef: A =:= B =
substitute[=:=[A, *]](implicitly[A =:= A])
}

sealed abstract class IsInstances {
Expand All @@ -78,7 +86,7 @@ sealed abstract class IsInstances {
}
}

object Is extends IsInstances {
object Is extends IsInstances with IsSupport {

/**
* In truth, "all values of `A Is B` are `refl`". `reflAny` is that
Expand All @@ -102,11 +110,10 @@ object Is extends IsInstances {

/**
* It can be convenient to convert a `Predef.=:=` value into an `Is` value.
* This is not strictly valid as while it is almost certainly true that
* `A =:= B` implies `A Is B` it is not the case that you can create
* evidence of `A Is B` except via a coercion. Use responsibly.
* This is not actually unsafe, but was previously labeled as such out
* of an abundance of caution
*/
@deprecated("use Is.isFromPredef", "2.2.0")
@inline def unsafeFromPredef[A, B](eq: A =:= B): A Is B =
reflAny.asInstanceOf[A Is B]

Is.isFromPredef(eq)
}
9 changes: 9 additions & 0 deletions tests/src/test/scala/cats/tests/AsSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ class AsSuite extends CatsSuite {
implicitly[String <~< AnyRef]
implicitly[(String, Int) <~< (AnyRef, Any)]
implicitly[scala.collection.immutable.List[String] <~< scala.collection.Seq[Any]]

{
trait Foo
trait Bar
implicit def subFooBar: Foo <:< Bar = implicitly[Foo <:< Foo].asInstanceOf[Foo <:< Bar]
// make sure the above is found
implicitly[As[Foo, Bar]]
val res: Foo <:< Bar = implicitly[As[Foo, Bar]].toPredef
}
}

trait Top {
Expand Down
11 changes: 10 additions & 1 deletion tests/src/test/scala/cats/tests/IsSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ class IsSuite extends CatsSuite {
val flip: Leibniz[Bar, Bar] = lifted.flip
val lift: Leibniz[List[Bar], List[Bar]] = lifted.lift[List]
val coerce: Bar = lifted.coerce(new Bar {})
val predefEq: =:=[Bar, Bar] = lifted.predefEq
val predefEq: =:=[Bar, Bar] = lifted.toPredef

{
trait Foo
implicit def eqFooBar: Foo =:= Bar = implicitly[Foo =:= Foo].asInstanceOf[Foo =:= Bar]
// make sure the above is found
implicitly[Is[Foo, Bar]]

val res: Foo =:= Bar = implicitly[Is[Foo, Bar]].toPredef
}
}

}