simmsb / calamity

A library for writing discord bots in haskell
https://hackage.haskell.org/package/calamity
MIT License
109 stars 11 forks source link

Fix ambiguity error in `command` function #48

Closed MorrowM closed 3 years ago

MorrowM commented 3 years ago

Running the following command in GHCi gives an interesting error message

λ> :t (command @'[Member] "test" $ \(_ctx :: FullContext) _ -> pure ())

<interactive>:1:2: error:
    • Could not deduce (Calamity.Commands.Context.CalamityCommandContext
                          c0)
        arising from a use of ‘command’
      from the context: (Polysemy.Internal.Union.Find
                           (Polysemy.Final.Final IO) r,
                         Polysemy.Internal.Union.Find Calamity.Cache.Eff.CacheEff r,
                         Polysemy.Internal.Union.LocateEffect (Polysemy.Final.Final IO) r
                         ~ '(),
                         Polysemy.Internal.Union.LocateEffect Calamity.Cache.Eff.CacheEff r
                         ~ '())
        bound by the inferred type of
                   it :: (Polysemy.Internal.Union.Find (Polysemy.Final.Final IO) r,
                          Polysemy.Internal.Union.Find Calamity.Cache.Eff.CacheEff r,
                          Polysemy.Internal.Union.LocateEffect (Polysemy.Final.Final IO) r
                          ~ '(),
                          Polysemy.Internal.Union.LocateEffect Calamity.Cache.Eff.CacheEff r
                          ~ '()) =>
                         Polysemy.Internal.Sem
                           (DSLState FullContext r)
                           (Calamity.Commands.Types.Command FullContext)
        at <interactive>:1:1
      The type variable ‘c0’ is ambiguous
      These potential instances exist:
        instance Calamity.Commands.Context.CalamityCommandContext
                   FullContext
          -- Defined at Calamity/Commands/Context.hs:68:10
        ...plus one instance involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: command @'[Member] "test"
      In the expression:
        (command @'[Member] "test" $ \ (_ctx :: FullContext) _ -> pure ())     

GHC complains about some c0 type variable that doesn't appear anywhere else. Interestingly, this only appears for certain types. If we replace Member with Int then it typechecks. After some digging, the culprit seems to be

type TypedCommandC ps c a r =
   [...]
  , ParameterInfoForParsers ps r
  )
...
class ParameterInfoForParsers (ps :: [Type]) r where
...
instance (ParameterParser x c r, ParameterInfoForParsers xs r) => ParameterInfoForParsers (x : xs) r where
...

Aha! We've introduced a new type variable c that's unrelated to the one mentioned in the type signature of command, leading to the ambiguity error. ParameterParser in turn requires ParameterParser in certain instances. Namely, the instances for types that cause the expression to not typecheck (such as Member). The fix is simple, add c to the typeclass parameters of ParameterInfoForParsers to ensure it's the same c.

simmsb commented 3 years ago

Thanks for fixing this!