[Haskell-cafe] type variable is ambiguous in a non-injective type family

Anthony Clayden anthony_clayden at clear.net.nz
Tue Jul 30 11:07:34 UTC 2019

    Anthony> I am interested in how you got into such a pickle in the
first place:

    Anthony> Why set `AllowAmbiguousTypes`? Did you understand what that means?
>I read
> which seems to indicate that using it is "a perfectly reasonable thing"

Hmm, I don't think that's how you got into the pickle. That SO answer
(and there are plenty on the subject) says the "combination" of
`AllowAmbiguousTypes` + `TypeApplications` is reasonable.

But your first post had `AllowAmbiguousTypes` without
`TypeApplications`. That is not "a perfectly reasonable thing"; no SO
answer says it is. If you had already looked at that SO answer, how
did you not use `TypeApplications`? Let me suggest how you got into
the pickle, because there is a repeated experience here which is poor
for learners:

You wrote the method signature for `toBool`. That's ambiguous. GHC
gave you an error message suggesting `AllowAmbiguousTypes` might get
the method signature to compile -- which it did.

`AllowAmbiguousTypes` works at the declaration site, not the usage
site. Which is why:

* It doesn't make sense to switch it on everywhere -- as one of the SO
comments suggests.
* It does need you to take action at the usage site, but GHC's message
doesn't tell you that.
* So your first post showed more `ambiguous` rejections at the usage site.
* What's more there was a red herring about non-injective type families.
* And to fix it per Li-yao's suggestion needs a whole bunch more extensions,
* including the deeply flawed `ScopedTypeVariables`,
* when the simpler `PatternSignatures` would be more appropriate --
except that's now deprecated.
* `AllowAMbiguousTypes` is module-wide: that means you get no
validation of any of your method sigs;
  although probably you mean only a few of the to be ambiguous.
* Then any/every usage site for any method might turn out to be ambiguous.

IMO GHC has got itself into a total mess in this bit of the language,
and the error messages lead learners rapidly into deep water. I think
`AllowAmbiguousTypes` might as well be spelled `AllhopeAbandonTypes`.

Those other extensions might be the best approach for your full
requirements, but might not.

So as to other possible designs:

* I didn't want to get into the FunDeps vs TypeFamilies war.

* If method `toBool` is only going to be called on the result of
`toSubAmbi`, I would go

>      toBool :: a -> Bool   -- not ambiguous
>    instance Ambi blah  where
>      type SubAmbi blah = ...
>      toSubAmbi x = ...
>      toBool x = case toSubAmbi x of { ... -> True; _ -> False }

Then in your `whatsWrong` function, call only `toBool`. So the
instances are a little more verbose but the usage sites are more
succinct and don't need all those extensions.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/haskell-cafe/attachments/20190730/f158e08b/attachment.html>

More information about the Haskell-Cafe mailing list