-
Notifications
You must be signed in to change notification settings - Fork 706
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
[WIP] #1234 - Leibniz & Liskov #1314
Conversation
I have used it in such context before. |
@TomasMikula My concern is that it adds a lot of noise to an otherwise simple idea, makes type-inference harder, and makes some of the user-facing methods harder to use because you have to supply them with additional type parameters (e.g. Overall it feels like it's a marginal improvement with a non-trivial cost. The same constraints class X[A <: B](a: A)
// can be replaced by:
class X[A](a: A, ev: A <~< B)
// or if the evidence is not required after construction:
class X[A] private[X] (a: A)
object X {
def apply[A](a: A)(implicit ev: A <~< B): X[A] = ???
}
What if we had Another option is Technically, |
The problem is when the type constructor with type bounds is not under your control, so you can't change it. Do we say "f*** you" to all such use cases? Aside: I think Scala's bounds checking is unnecessarily strict. If you have class Foo[A <: Bar] I believe you should be allowed to talk about |
What do you think of the solutions I outlined at the end of my previous post? I am mostly concerned with type-inference on methods and combinators and I'd like to optimize for the most common case of P.S.
Well, it is not like every single data type taking kind
That would raise some interesting questions like: trait Bar { type Out = String }
trait Baz { type Out = Boolean }
class Foo [A <: Bar] { type Out = A#Out }
type X = Foo[Baz]#Out // what is X? |
I like the one with wrapping. Not sure how "all methods on syntax traits" helps if
Good point. For that the constraint would have to be checked. |
This is roughly what I had in mind. Turns out there is quite a bit of duplication needed to completely avoid allocations (calling methods on |
If |
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.
For the sake of moving forward, I propose to merge this and add type bounds if/when needed.
Sorry for neglecting this PR, I've been experimenting with |
Some of the new "inventions" and old goodies:
I also found a way to emulate union types on Scala2 and I think it can be made safe but it is extremely mmm hacky in its appearance. Useful if you want to turn |
If you want to replace this PR with your new
In the last two gists there doesn't seem to be any guarantee that the types you are asserting to be equal are singleton types.
This is nice. Could it be used to prove something about types that are not fully known at compile time? E.g. how would you prove that
I suppose you meant |
Okay, awesome. I'll get it done this week.
Right, those are early prototypes before I got into singleton types 😉
Well, ideally you would have the compiler (or a compiler plugin) figure out that since the proof is total and equality is a singleton (as in there is only one implementation according to free theorems), you can just "force" it for arbitrary def assertTotal[P[_ <: Nat, _<: Nat]: Proof]
(proof: [M <: Nat, N <: Nat] (M, N) => P[N, M]): P[N, M]
Yep. |
Conflicts: base/src/main/scala/scalaz/typeclass/Liskov.scala base/src/main/scala/scalaz/typeclass/LiskovFunctions.scala base/src/main/scala/scalaz/typeclass/LiskovInstances.scala base/src/main/scala/scalaz/typeclass/LiskovTypes.scala
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.
Overall, great stuff!
* explicitly coerce types. It is unsafe, but needed where Leibnizian | ||
* equality isn't sufficient. | ||
*/ | ||
def unsafeForce[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.
Why does this unsafeForce
not need @SuppressWarnings(Array("org.wartremover.warts.AsInstanceOf"))
, while all of the others do?
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.
Scalaz doesn't use WartRemover as far as I can tell, I will simply remove this annotation. All asInstanceOf
require it, I was just being lazy with warnings in leibniz
😉
// final def liftCt[F[-_]]: F[B] <~< F[A] = { | ||
// type f[+α] = F[α] <~< F[A] | ||
// substCo[f](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.
👎 for commented-out code.
It looks like CI checks failed because it couldn't download SBT. |
Sorry for waiting so long, I just wanted to give others the chance to review. If you find time to resolve the conflicts, I will merge this. If not, I will try to resolve them myself. Thanks! |
I have ported some of the original Leibniz / Liskov functionality over to scalaz8 branch.
Leibniz
are needed. I understand that without themsubst[F]
onF[_ <: X]
won't work, but is it really ever used in such a context?Leibniz
to higher-kinded types (e.g. equality between type constructors).lower
functions yet since there is noInjective
typeclass.Liskov
companion object functions are still missing....Functions
trait?=:=
and<:<
might have most of the required functionality (most importantlysubstitute
) in 2.13.Leibniz
is from the article.