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

Type classes, functional dependencies, and arity #532

Open
noughtmare opened this issue Oct 18, 2022 · 1 comment
Open

Type classes, functional dependencies, and arity #532

noughtmare opened this issue Oct 18, 2022 · 1 comment

Comments

@noughtmare
Copy link

noughtmare commented Oct 18, 2022

I was just writing some code involving type classes:

import Control.Monad.Except

class InSum x xs where
  inject :: x -> xs

instance InSum x (Either x xs) where
  inject = Left

instance InSum x xs => InSum x (Either y xs) where
  inject = Right . inject

throw :: (InSum e es, MonadError es m) => e -> m a
throw = throwError . inject

data ErrorA = ErrorA deriving Show
data ErrorB = ErrorB deriving Show
data Result
data Input

x :: Input
x = undefined

f :: forall es m . (InSum ErrorA es, MonadError es m) => Input -> m Result
f = throw ErrorA

This produces the error:


T.hs:25:5: error:
    • Could not deduce (InSum ErrorA es0) arising from a use of ‘throw’
      from the context: (InSum ErrorA es, MonadError es m)
        bound by the type signature for:
                   f :: forall es (m :: * -> *).
                        (InSum ErrorA es, MonadError es m) =>
                        Input -> m Result
        at T.hs:24:1-74
      The type variable ‘es0’ is ambiguous
      Potentially matching instances:
        instance InSum x (Either x xs) -- Defined at T.hs:7:10
        instance InSum x xs => InSum x (Either y xs)
          -- Defined at T.hs:10:10
    • In the expression: throw ErrorA
      In an equation for ‘f’: f = throw ErrorA
   |
25 | f = throw ErrorA
   |     ^^^^^

I'd say this error message is mostly useless. For who hasn't spotted the mistake yet: the issue is that f should take one argument, but I've not written that explicitly so now it thinks the monad of throw is the function monad. So the simple fix is to write:

f _ = throw ErrorA

The order of the constraints I chose in throw :: (InSum e es, MonadError es m) => e -> m a makes the error message much worse. If I swap those two constraints I get the much more useful error message:

T.hs:25:5: error:
    • Could not deduce (MonadError es0 ((->) Input))
        arising from a use of ‘throw’
      from the context: (InSum ErrorA es, MonadError es m)
        bound by the type signature for:
                   f :: forall es (m :: * -> *).
                        (InSum ErrorA es, MonadError es m) =>
                        Input -> m Result
        at T.hs:24:1-74
      The type variable ‘es0’ is ambiguous
    • In the expression: throw ErrorA
      In an equation for ‘f’: f = throw ErrorA
   |
25 | f = throw ErrorA
   |     ^^^^^

Still, I would expect in this case the suggestion that there might be something wrong with the arity of a function somewhere.

I think the simplest way to improve this error message is just to include all unsolved constraints in the error message. Or at least those that mention the same variables as the "main" unsolved constraint. And especially if another constraint has a functional dependency that would determine the concrete type if it were to be resolved.

@noughtmare noughtmare changed the title Type classes and arity Type classes, functional dependencies, and arity Oct 18, 2022
@goldfirere
Copy link
Collaborator

That sounds like a very reasonable, implementable suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants