-
Notifications
You must be signed in to change notification settings - Fork 108
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
Use TypeError to improve type error messages. #265
Conversation
Adds an overlapping instance of `HasField` which gives a better error message when no instance can be found. Compare these results to: https://gist.github.com/judah/e6678257cc28f2c486a631e49db885f2#file-lens-errors-md New error message for the expression `def & value .~ 3 :: DescriptorProto` : ``` <interactive>:6:14: error: • Type DescriptorProto has no field named "value" • In the first argument of ‘(.~)’, namely ‘#value’ In the second argument of ‘(&)’, namely ‘#value .~ "hello"’ In the expression: defMessage & #value .~ "hello" :: DescriptorProto ``` New error message for the expression `() & #name .~ "hello"`: ``` <interactive>:6:6: error: • Type () has no field named "name" • In the first argument of ‘(.~)’, namely ‘#name’ In the second argument of ‘(&)’, namely ‘#name .~ "hello"’ In the expression: () & #name .~ "hello" Previous error message: ``` In order for this improvement, needed to change the HasField' class slightly, since overlapping instances don't work with fundeps (or, similarly, open type families). For example, previously we had: ``` class HasField' s (x :: Symbol) a | s x -> a where ... instance HasField' Foo "fieldA" Int32 where ... instance HasField' Foo "fieldB" Text where ... ``` In this change, we remove the fundep and move that constraint into each instance: ``` class HasField' s (x :: Symbol) a where ... instance a ~ Int32 => HasField' Foo "fieldA" where ... instance a ~ Text => HasField' Foo "fieldB" a where ... ``` I don't think this is a big burden since all those instances are generated automatically, and we also put in the Haddocks a more human-readable list of fields for each type.
I'm not sure why CI is failing for the Cabal builds; maybe something changed in the PPA upstream? It seems unrelated to this change, though, since all the Stack builds are passing. |
Agreed, the error is indicative of some infrastructure problem. My guess would be that apt-get suddenly became stricter and no longer allows sha1:
|
lens-labels/src/Lens/Labels.hs
Outdated
-- be defined such that @a@ is derivable from @s@ and @x@. For example, to define a | ||
-- field named @"x"@ of type @Int32@ for the containing type @Foo@: | ||
-- | ||
-- > instance a ~ Int32 => HasLens' Foo "x" a where ... |
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.
Could you comment on why this is preferrable
instance a ~ Int32 => HasLens' Foo "x" a where ...
to this:
instance HasLens' Foo "x" Int32 where ...
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 I recognized the constraint trick, still makes sense to document it as such.
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.
Added more explanation.
lens-labels/src/Lens/Labels.hs
Outdated
type ASetter s t a b = LensLike Identity s t a b | ||
|
||
(.~), set :: ASetter s t a b -> b -> s -> t | ||
f .~ x = f %~ const x | ||
f .~ x' = f %~ const x' |
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 prime required? Did we magic up an x
in global scope somehow?
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.
Removed. (It was part of debugging that I forgot to remove.)
-- lensOf _ = ... | ||
-- Note: for optional fields, this generates an instance both for "foo" and | ||
-- for "maybe'foo" (see plainRecordField below). | ||
[ uncommented $ instDecl [] | ||
[ uncommented $ instDecl [equalP "a" (tyParen t)] |
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 results in a ~ Bar
rather than Bar ~ a
in the comment above. Make them consistent?
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.
Good catch. Switched the comment.
@@ -86,10 +86,13 @@ instance | |||
-- Note: in order to support better type error messages, this class does not have | |||
-- a fundep relationship to the parameter @a@. Instead, instances are expected to | |||
-- be defined such that @a@ is derivable from @s@ and @x@. For example, to define a | |||
-- field named @"x"@ of type @Int32@ for the containing type @Foo@: | |||
-- field named @"r"@ of type @Int32@ for the containing type @Foo@, use: |
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.
Might as well give it a longer name to reduce the chance of collision?
Adds an overlapping instance of `HasField` which gives a better error message when no instance can be found. Compare these results to: https://gist.github.com/judah/e6678257cc28f2c486a631e49db885f2#file-lens-errors-md New error message for the expression `def & value .~ 3 :: DescriptorProto` : ``` <interactive>:6:14: error: • Type DescriptorProto has no field named "value" • In the first argument of ‘(.~)’, namely ‘#value’ In the second argument of ‘(&)’, namely ‘#value .~ "hello"’ In the expression: defMessage & #value .~ "hello" :: DescriptorProto ``` New error message for the expression `() & #name .~ "hello"`: ``` <interactive>:6:6: error: • Type () has no field named "name" • In the first argument of ‘(.~)’, namely ‘#name’ In the second argument of ‘(&)’, namely ‘#name .~ "hello"’ In the expression: () & #name .~ "hello" Previous error message: ``` In order for this improvement, needed to change the HasField' class slightly, since overlapping instances don't work with fundeps (or, similarly, open type families). For example, previously we had: ``` class HasField' s (x :: Symbol) a | s x -> a where ... instance HasField' Foo "fieldA" Int32 where ... instance HasField' Foo "fieldB" Text where ... ``` In this change, we remove the fundep and move that constraint into each instance: ``` class HasField' s (x :: Symbol) a where ... instance a ~ Int32 => HasField' Foo "fieldA" where ... instance a ~ Text => HasField' Foo "fieldB" a where ... ``` I don't think this is a big burden since all those instances are generated automatically, and we also put in the Haddocks a more human-readable list of fields for each type.
…oogle#310) This reverts commit 60b7c3c. It turned out that the TypeError would sometimes fire incorrectly, even when there was transitive instance available. Details: google@4bb77a5 I suspect a GHC bug, but wasn't easily able to simplify the example enough to know for sure.
Adds an overlapping instance of `HasField` which gives a better error message when no instance can be found. Compare these results to: https://gist.github.com/judah/e6678257cc28f2c486a631e49db885f2#file-lens-errors-md New error message for the expression `def & value .~ 3 :: DescriptorProto` : ``` <interactive>:6:14: error: • Type DescriptorProto has no field named "value" • In the first argument of ‘(.~)’, namely ‘#value’ In the second argument of ‘(&)’, namely ‘#value .~ "hello"’ In the expression: defMessage & #value .~ "hello" :: DescriptorProto ``` New error message for the expression `() & #name .~ "hello"`: ``` <interactive>:6:6: error: • Type () has no field named "name" • In the first argument of ‘(.~)’, namely ‘#name’ In the second argument of ‘(&)’, namely ‘#name .~ "hello"’ In the expression: () & #name .~ "hello" Previous error message: ``` In order for this improvement, needed to change the HasField' class slightly, since overlapping instances don't work with fundeps (or, similarly, open type families). For example, previously we had: ``` class HasField' s (x :: Symbol) a | s x -> a where ... instance HasField' Foo "fieldA" Int32 where ... instance HasField' Foo "fieldB" Text where ... ``` In this change, we remove the fundep and move that constraint into each instance: ``` class HasField' s (x :: Symbol) a where ... instance a ~ Int32 => HasField' Foo "fieldA" where ... instance a ~ Text => HasField' Foo "fieldB" a where ... ``` I don't think this is a big burden since all those instances are generated automatically, and we also put in the Haddocks a more human-readable list of fields for each type.
…oogle#310) This reverts commit 60b7c3c. It turned out that the TypeError would sometimes fire incorrectly, even when there was transitive instance available. Details: google@4bb77a5 I suspect a GHC bug, but wasn't easily able to simplify the example enough to know for sure.
Adds an overlapping instance of
HasField
which givesa better error message when no instance can be found.
Compare these results to:
https://gist.github.com/judah/e6678257cc28f2c486a631e49db885f2#file-lens-errors-md
New error message for the expression
def & value .~ 3 :: DescriptorProto
:New error message for the expression
() & #name .~ "hello"
:In order for this improvement, needed to change the HasField' class slightly,
since overlapping instances don't work with fundeps (or, similarly, open type
families).
For example, previously we had:
In this change, we remove the fundep and move that constraint into
each instance:
I don't think this is a big burden since all those instances are
generated automatically, and we also put in the Haddocks a more
human-readable list of fields for each type.
This change is