Skip to content
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

Merged
merged 2 commits into from
Dec 2, 2018

Conversation

judah
Copy link
Collaborator

@judah judah commented Nov 29, 2018

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.


This change is Reviewable

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.
@judah
Copy link
Collaborator Author

judah commented Nov 29, 2018

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.

@blackgnezdo
Copy link
Collaborator

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:

Reading package lists...
W: http://ppa.launchpad.net/couchdb/stable/ubuntu/dists/trusty/Release.gpg: Signature by key 15866BAFD9BCC4F3C1E0DFC7D69548E1C17EAB57 uses weak digest algorithm (SHA1)
The command "sudo -E apt-get -yq --no-install-suggests --no-install-recommends $(travis_apt_get_options) install cabal-install-2.0 ghc-8.2.1" failed and exited with 100 during .

-- 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 ...
Copy link
Collaborator

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 ...

Copy link
Collaborator

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added more explanation.

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'
Copy link
Collaborator

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?

Copy link
Collaborator Author

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)]
Copy link
Collaborator

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?

Copy link
Collaborator Author

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:
Copy link
Collaborator

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?

@judah judah merged commit 60b7c3c into google:master Dec 2, 2018
@judah judah deleted the type-error-messages branch December 2, 2018 03:49
judah added a commit that referenced this pull request Feb 1, 2019
This reverts commit 60b7c3c.

It turned out that the TypeError would sometimes fire incorrectly,
even when there was transitive instance available.  Details:
4bb77a5

I suspect a GHC bug, but wasn't easily able to simplify the example enough to know
for sure.
judah added a commit that referenced this pull request Feb 2, 2019
This reverts commit 60b7c3c.

It turned out that the TypeError would sometimes fire incorrectly,
even when there was transitive instance available.  Details:
4bb77a5

I suspect a GHC bug, but wasn't easily able to simplify the example enough to know
for sure.
avdv pushed a commit to avdv/proto-lens that referenced this pull request Aug 9, 2023
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.
avdv pushed a commit to avdv/proto-lens that referenced this pull request Aug 9, 2023
…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.
ylecornec pushed a commit to ylecornec/proto-lens that referenced this pull request Feb 19, 2024
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.
ylecornec pushed a commit to ylecornec/proto-lens that referenced this pull request Feb 19, 2024
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants