-
Notifications
You must be signed in to change notification settings - Fork 570
Turn Coercible
into a symmetric relation
#3930
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
Turn Coercible
into a symmetric relation
#3930
Conversation
I'm not qualified to catch subtle errors in this part of the code, but from what I can tell it all seems reasonable to me! Maybe a few more cases worth testing?
|
I added more tests. Thank you for the suggestion, this made me notice that I wasn’t looking for |
isSymmetricCoercibleDictInScope a b TypeClassDictionaryInScope{..} = tcdInstanceTypes == [b, a] | ||
|
||
srcCoercibleTransitiveConstraint k a b TypeClassDictionaryInScope{..} | ||
| [a', b'] <- tcdInstanceTypes = |
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'm a little bit unsure about this approach. Could it lead to a loop? Maybe we should be following section 5 of the paper a bit more closely here? Transitivity is hard because it's not always going to be clear what type to pick for b
to get from a
to c
, so I think we should expect the solver to fail to solve Coercible in at least some of the harder cases which require use of transitivity rule. Perhaps we should just do symmetry on its own first?
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.
Yeah, along those lines, here's a case for which the patch currently produces a PossiblyInfiniteInstance:
test :: forall a b c d. Coercible a c => Coercible a d => Coercible d b => Proxy c -> Proxy d -> a -> b
test _ _ = coerce
The equivalent Haskell works, though I know nothing about how what this patch does differs from what GHC is doing.
If a temporary halfway solution is a good idea, though, wouldn't it be better to have the simpler transitive cases covered with some weirder ones producing PossiblyInfiniteInstance errors then to have all transitive cases produce NoInstanceFound errors?
Maybe a counterargument to that is that this transitive search can produce PossiblyInfiniteInstances where NoInstanceFound would be correct and clearer, in cases like forall a. Coercible a String => a -> Int
. Does that matter much? I think maybe it does.
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.
Yeah, I think producing PossiblyInfiniteInstance when we should be producing NoInstanceFound ought to be considered a bug. I think temporary halfway solutions are best avoided in the solver in general, as fixing a temporary halfway solution to work properly might mean that things which used to type check no longer do, and pushing out a change like that is usually going to ruin a few people’s days.
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.
Oh that’s no good, I‘ve undone transitivity. Thank you for noticing!
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 record, I‘ve read the section 5 Type inference with Coercible constraints of the paper and I‘m trying to rewrite the solver of Coercible
constraints to support much richer interactions of givens and wanteds, as explained in the paper.
Coercible
into a symmetric and transitive relationCoercible
into a symmetric relation
@hdgarrood Is there still a chance this will make it into 0.14? I'd like to avoid the churn of a bunch of libraries adding |
That's a good point, yes. Let's try to get this into 0.14 then. There's still time - it will take a little while until we are ready to release v0.14.0 properly, as there are still a bunch of library updates which will need doing. |
I've encountered a case that will suffer by making this symmetric:
I'm not saying we shouldn't do it still, as perhaps this falls outside the motivation of |
Being able to rewrite arbitrary type parameters safely requires Edit: I suppose Leibniz wouldn’t have the right roles to work with coerce, but the point still stands for other types with invariant type parameters, like |
Yes, I would say that Json falls outside the realm of Coercible. It won’t be possible to write any Coercible instances for Json anyway, since the instances are generated on the fly and the compiler forbids you from writing any yourself. In general I think we should aim to follow the safe zero-cost coercions paper as closely as possible, and in the paper Coercible is a symmetric relation, as it only holds where the two types always have the same runtime representation in a way that is verifiable by the compiler. |
That all makes sense, 'scuse the silly question! |
Not at all, I'm very happy to answer questions :) |
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.
Code looks good to me, just a minor quibble about the docs.
src/Language/PureScript/Docs/Prim.hs
Outdated
, "First, as a trivial base-case, reflexivity - any type has the same" | ||
, "representation as itself:" | ||
, "First, Coercible obeys reflexivity, any type has the same representation" | ||
, "as itself and symmetry, pairs of types can be coerced both ways:" |
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 there should be a comma after "itself" here, as at the moment it sounds like the definition of reflexivity is "any type has the same representation as itself and symmetry", which doesn't really make sense. Although even with that comma, it's still a bit clunky. I think it might be best to address symmetry in a separate paragraph:
First, Coercible obeys reflexivity - any type has the same representation as itself:
instance coercibleReflexive :: Coercible a a
Second, Coercible obeys symmetry - if a type
a
can be coerced to some other typeb
, thenb
can also be coerced back toa
:instance coercibleSymmetric :: Coercible a b => Coercible b 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 wanted to avoid updating the following rules numbering but this is way better.
507f634
to
2dc850d
Compare
2dc850d
to
2cc96f7
Compare
@rhendric wondered in purescript/purescript-newtype#22 (comment) why
Coercible a b
doesn’t implyCoercible b a
, and indeed it seems like it should.The Safe Zero-cost Coercions for Haskell paper states in 2 The design and interface of Coercible that
Coercible
is an equivalence relation and hence should obey the following rules:This is the case in Haskell:
But our
Coercible
obeys neither symmetrynor transitivity