Solve Coercible constraints on rows#3878
Conversation
8f5f0b5 to
3ab8a86
Compare
| unless (null (fst rl1) && null (fst rl2)) $ | ||
| throwError . errorMessage $ TypesDoNotUnify (rowFromList rl1) (rowFromList rl2) |
There was a problem hiding this comment.
I think we should add tests for coercing polymorphic rows.
test :: forall r. { a :: Int | r } -> { a :: Id1 Int | r }
test = coerceI don't think there's a reason this shouldn't coerce since the tails unify.
test :: forall r s. { a :: Int | r } -> { a :: Id1 Int | s }
test = coerceI'd think this needs to introduce a subgoal for Coercible r s, but Coercible isn't polykinded, so I assume we'll need to reject this with a unification error.
There was a problem hiding this comment.
Should we consider having special casing for records so that it introduces a subgoal Coercible (Record r) (Record s)?
There was a problem hiding this comment.
I think a unification error is fine, since it's likely an error anyway. For example you couldn't write:
test :: forall r s. { a :: Int | r } -> { a :: Int | s }
test = identity
The way to solve that would be a type equality that r ~ s.
There was a problem hiding this comment.
Right, that makes sense.
There was a problem hiding this comment.
Actually, maybe a unification error isn't right, and we should just punt the whole constraint? It's possible that it could be solved at the call site.
There was a problem hiding this comment.
Given
test :: forall r s. { a :: Int | r } -> { a :: Id1 Int | s }
test = coerceWe are solving the instantiation of coerce. With the provided signature, the constraint is not solvable because r does not unify with s. We shouldn't throw a unification error because it's possible that it could be solved, given that r does unify with s. That is, this is potentially solvable:
test :: forall r s. Coercible { a :: Int | r } { a :: Id1 Int | s } => { a :: Int | r } -> { a :: Id1 Int | s }
test = coerce
test2 = test { a: 42 } :: { a :: Id1 Int }because now r and s unify with (). We should not throw a unification error, we should just defer the constraint.
There was a problem hiding this comment.
Note that I think it's fine to throw a unification error for the existing case as written, because we know for a fact there is no solution, since there are concrete rows that differ. I'm only talking about polymorphic tails.
There was a problem hiding this comment.
Right, I see, thanks. That approach seems a bit odd to me because then test effectively has an invisible Coercible constraint, right? If I wrote a type annotation forall r s. { a :: Int | r } -> { a :: Id1 Int | s } then I would want the compiler to tell me if that function can’t actually be used for all possible choices of r and s; this feels like a parametricity violation to me.
There was a problem hiding this comment.
I'm not sure what you mean by "an invisible Coercible constraint". I'm just saying that the compiler should not fail with a unification error, it should fail saying that it can't solve the constraint, and so the user should add the constraint if they want it to solve.
There was a problem hiding this comment.
Oh! I see now. That sounds reasonable to me, yes.
|
I wasn’t considering rows tails so open rows were coercible regardless of whether their tails unified 🙃 I rethrown a Also I special cased records instead of rows because in error messages is misleading: the compiler throws a This means that we can’t solve coercible constraints on user written types parametrized by rows though, such as |
Coercible constraints on rowsCoercible constraints on records
|
I think the case for records is fine for now. I don't think it should hold up a release of it, and we can continue to discuss solutions in a separate issue. |
|
This looks good to me too, but I'd quite like to get #3873 in first since I think it addresses a more fundamental problem with how we're dealing with roles inside the compiler, if that sounds ok? |
These are really good questions. I was thinking about them a bit more and this led me to discover #3889. Shall we continue this conversation there? |
|
I would like to come back to this once #3893 is in, since that addresses a more fundamental issue with Coercible instances for parameterized data types, and will probably have implications for the approach we want to take here. |
8b6ea59 to
f1fc5ab
Compare
Coercible constraints on recordsCoercible constraints on rows
|
I’ve rebased this branch! The changes to the instance solver are quite minimal now that |
src/Language/PureScript/Docs/Prim.hs
Outdated
| , "This rule may seem puzzling since it is impossible to apply `coerce` to a term" | ||
| , "of type `NT1` but it is necessary to coerce types with higher kinded parameters." | ||
| , "" | ||
| , "Fifth, every pair of rows can be coerced if the types they contain with" |
There was a problem hiding this comment.
How about this?
Fifth, every pair of rows can be coerced if they have the same labels and the corresponding types for each label are coercible.
There was a problem hiding this comment.
That‘s better indeed, I‘ve applied your suggestion.
8648ea3 to
4530792
Compare
|
Thanks! Would you mind merging master so that CI will hopefully pass? |
4530792 to
7897537
Compare
|
I rebased on top of master, tests pass again 😌 |
|
I think I'll leave this open for a little bit in case anyone with better knowledge of the solver wants to weigh in; I'm not particularly familiar with it right now. |
|
I'm going to merge this for now so that we can get an RC out. |
Close #3875.