-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Adds an As class which represents subtyping relationships (Liskov) #1564
Conversation
This is a direct port of the Liskov class from scalaz. I named it `As` to be similar to our new `Is` class representing type equality, but there are aliases named `<~<` and `Liskov`
* to a function, such as F in: `type F[-B] = B => String`. In this | ||
* case, we could use A As B to turn an F[B] Into F[A]. | ||
*/ | ||
def subst[F[-_]](p: F[B]): F[A] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use substitute in Is.
* The original contribution to scalaz came from Jason Zaugg | ||
*/ | ||
sealed abstract class As[-A, +B] { | ||
def apply(a: A): B = As.witness(this)(a) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we called this coerce in Is. Can we use the same for both?
/** | ||
* Use this relationship to widen the output type of a Function1 | ||
*/ | ||
def onF[X](fa: X => A): X => B = As.co2_2[Function1, B, X, A](this)(fa) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this one on the instance but generally the rest are on the object?
/* | ||
* Subtyping forms a category | ||
*/ | ||
implicit val liskov: Category[<~<] = new Category[<~<] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a category for Is. we should be consistent.
/** | ||
* Subtyping is reflexive | ||
*/ | ||
implicit def refl[A]: (A <~< A) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this and isa?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, in Is we use a singleton to avoid allocations.
new (A <~< A) { | ||
def subst[F[-_]](p: F[A]): F[A] = p | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have a method to materialize a <:<
as needed. I think we should. Also a similar unsafe method to go from <:<
to As.
def co2[T[+_, _], Z, A, B](a: A <~< Z): T[A, B] <~< T[Z, B] = | ||
a.subst[λ[`-α` => T[α, B] <~< T[Z, B]]](refl) | ||
|
||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have all these methods below I think we should have the analogous for Is.
@@ -2,4 +2,24 @@ package cats | |||
|
|||
package object evidence { | |||
type Leibniz[A, B] = cats.evidence.Is[A, B] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we use cats.evidence here but not below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also add a ===
alias to parallel <~<
?
/** | ||
* Unsafely force a claim that A is a subtype of B | ||
*/ | ||
def force[A, B]: A <~< B = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we should have this. You can call isa and cast if you want to do something unsafe. The cast at least shows you it is unsafe. Burying an unsafe cast in a library function seems like a bad choice to me.
remove explicit reference to package in Is type alias (for posco) add === type alias for `Is` (for sellout)
* Lift Scala's subtyping relationship | ||
*/ | ||
@inline def unsafeFromPredef[A, B >: A]: (A As B) = | ||
reflAny.asInstanceOf[A As B] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is actually safe (you could allocate A As B here). It is going from A <:< B
that I think is correct but can't be proven via the types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, start with refl, then do to covariance you have A As A <: A As B
so it can be returned. So I think returning refl[A]
here will compile.
|
||
class AsTests extends CatsSuite { | ||
import evidence._ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we test resolution explicitly of a few types? Int As Any
(Int, Long) As (Any, Any)
- change unsafeFromPredef to just use refl - add tests for some expected relationships such as Int <~< Any, and List[String] <~< List[Any]
@johnynek I think I've covered all your requests now |
I think implicit def reify[A, B >: A]: A <~< B = refl[A]
def fromPredef[A, B](eq: A <:< B): A <~< B =
unsafeForce[A, B] |
I agree with @alexknvl 's comments. I don't see how what we have currently called Also, the coverage of this PR is pretty low. I love using this: to see which methods have no tests. Very helpful (even a fun game to get everything green). |
I think this is the last thing that needs to be merged to get a Monocle-Cats PR submitted. |
Codecov Report
@@ Coverage Diff @@
## master #1564 +/- ##
==========================================
+ Coverage 93.08% 93.93% +0.85%
==========================================
Files 250 242 -8
Lines 3989 4122 +133
Branches 136 161 +25
==========================================
+ Hits 3713 3872 +159
+ Misses 276 250 -26
Continue to review full report at Codecov.
|
… derrived from what we have, and I can't find anyone using them in the wild)
May I also suggest not adding |
|
@stew I found out the cause, the type inference in def toMap[A, B, X](fa: List[X])(implicit ev: X <~< (A,B)): Map[A,B] = {
type RequiredFunc = (Map[A, B], X) => Map[A, B]
type GivenFunc = (Map[A, B], (A, B)) => Map[A, B]
val subst: GivenFunc <~< RequiredFunc = As.contra2_3(ev) //introduced because inference failed on scalajs on 2.10.6
fa.foldLeft(Map.empty[A,B])(subst(_ + _))
} Do you have time to update the PR with this change? @johnynek do you mind taking another look? |
Thank you very much @kailuowang for the solutiongit diff
val f2: Any => Top = As.onF(cAsA)(f) | ||
} | ||
|
||
test("we can simultaneously narrow the input and widen the ouptut of a Function1") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like we are missing a test here?
@stew got a bit time to finish this up? If not I would be more than happy to work on it. |
* Adds an As class which represents subtyping relationships This is a direct port of the Liskov class from scalaz. I named it `As` to be similar to our new `Is` class representing type equality, but there are aliases named `<~<` and `Liskov` * incorporate suggestions from Oscar (Thanksgit diff --cached)git * delete-trailing-whitespace * Minor changes to evidence/packages.scala remove explicit reference to package in Is type alias (for posco) add === type alias for `Is` (for sellout) * More enhancements for posco's feedback (thanks!) - change unsafeFromPredef to just use refl - add tests for some expected relationships such as Int <~< Any, and List[String] <~< List[Any] * ome more * add more tests, contravariant tests still pending * add contra tests and get rid fo the liftF like functions (they can be derrived from what we have, and I can't find anyone using them in the wild) * don't try to link the type in the scaladoc * fix test failure on 2.10.6 + scalajs Thank you very much @kailuowang for the solutiongit diff * added helper methods for converting Function1 * added category law tests
This is a direct port of the Liskov class from scalaz. I named it
As
to besimilar to our new
Is
class representing type equality, but there are aliasesnamed
<~<
andLiskov