-
Notifications
You must be signed in to change notification settings - Fork 3
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
Improve inference #16
Conversation
Whew! Looks like I got this, pretty much. There are some ugly tricks to pull for 7.10, and I have no idea why 8.2 rejects my example (presumably a compiler bug), but it looks like the overall idea works. I'm guessing this is probably pretty close to McBride's original intention, which wasn't yet expressible in Haskell at the time. Inference should be quite powerful. |
The major limitation of this version is that newtypes with kind arguments aren't fully supported. That is, given |
@sjakobi, @chessai, do you think it would be okay to require GHC ≥ 8.4? |
I am definitely okay with that. |
@chessai any general opinions on this PR? |
@kcsongor has a filthy trick that can probably improve some of the type errors when people misuse these functions. |
@sjakobi Have you any thoughts? |
The original concept of this API is to work "over", "under", or "a la" a *single* newtype, potentially changing its type arguments but not changing it wholesale. Back in the day, Haskell couldn't express that idea, but now it can (for the most part). We can take advantage of that to improve inference and typed hole error messages. Fixes love-haskell#15
The error messages actually turned out to be much more reasonable than I feared! I do with that |
`Coercible` seems to be really fragile under GHC versions before 8.4. 8.2 seems especially fussy: even when the package compiles, use cases may fail mysteriously.
I am having a bit of trouble getting my custom type error to fire. I'll have to see if I can fix that. |
The custom "not a newtype" error wasn't showing up where I wanted it to. Make that happen.
All right. I think I fixed the custom error issue. I couldn't get @kcsongor's dirty trick to improve things here without simultaneously making other things worse, so I think we should skip that. The basic annoyance is that GHC (in |
Is that extra flexibility of not requiring |
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.
Wow, cool stuff! The type inference is great!
The whole thing goes a bit over my head though so I hope you don't mind if I ping you if issues come up, @treeowl.
-- @Const Int Char@ and @Const Int Maybe@ are not @Similar@ | ||
-- because they have different kind arguments. | ||
class Similar (n :: k) (n' :: k) | ||
instance (Similar' n n', Similar' n' n) => Similar n n' |
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 would be easier to read if the name Similar
was easier to distinguish from the name for Similar'
.
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.
Unfortunately, users will end up seeing Similar'
, which is fundamentally an implementation detail, in error messages and GHCi :t
responses. I don't know of any way to avoid this without causing other problems. So either I can document that internal thing or I can pick a name that's close enough for people to guess the meaning. Bleh!
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'd appreciate a bit of documentation for Similar'
in any case!
For sure. I should add some more comments. The trickiest bit is Similar' (F a b) n'
==>
(Similar' (F a) (GetFun n'), n' ~ GetFun n' (GetArg n')) We have now constrained things only a little: (Similar' (F a) p, n' ~ p q)
((Similar' F (GetFun p), p ~ GetFun p (GetArg p)), n' ~ p q) Let's set ((Similar' F r, p ~ r s), n' ~ p q) We've now reached the base case! Yay! So the last step is ((F ~ r, p ~ r s), n' ~ p q) which then simplifies to n' ~ F s q Undoing the substitutions we made, n' ~ F (GetArg (GetFun n')) (GetArg n') The arguments in that constraint are completely useless (as far as I know), but we need to put it all together like that to get what we're after: an equality constraint on type Rep (Sum a) = M1 _ _ (M1 _ _ (M1 _ _ (K1 _ a))) This can reduce as soon as GHC sees we have |
I'm definitely leaning toward requiring |
Force `o` to be the underlying type of `n`. I think this makes for a better story to tell users.
I've expanded the documentation considerably, and, in a separate commit, imposed the tighter constraints I mentioned. Let me know what you think. |
@sjakobi, have you had a chance to consider this further? Do you have any more questions? |
Thank you @treeowl! |
Sorry for not helping out here; I've been indisposed recently and didn't want to devote too much brainpower to any one task. |
@chessai No worries! Get well soon! :) |
The original concept of this API is to work "over", "under", or
"a la" a single newtype, potentially changing its type arguments
but not changing it wholesale. Back in the day, Haskell couldn't
express that idea, but now it can (for the most part). We can take
advantage of that to improve inference and typed hole error messages.
Fixes #15
Additionally, remove support for GHC versions before 8.4. These have pretty horribly brittle support for
Coercible
(7.10 being especially different and 8.2 being especially flaky), so supporting them is a bit nightmarish.