[Haskell-beginners] incoherent instance question

Graham Gill math.simplex at gmail.com
Sat Jun 9 01:12:46 UTC 2018


 Thank you Alex for that comprehensive answer. That's very helpful. I'd
considered a newtype, but I really wondered what prevented me from using or
was bad about the approach I described.

Regards,
Graham

On 04-Jun-2018 1:10 AM, Alex Rozenshteyn wrote:

I want to say that if a type is Bounded, then it is also UpperBounded and
> LowerBounded.


Seems reasonable


> If a type is both UpperBounded and LowerBounded, then it is also Bounded.


Danger Will Robinson. Nothing stops modules (realistically, in two
different library) from defining incompatible UpperBounded and LowerBounded
instances; for example, I may want `lowerBound :: Bool` to be `True`, while
you may want `upperBound :: Bool` to be `True`; when they are both
imported, bad things can happen. In this case, requiring an Ord constraint
and adding documentation on lawful instances would pretty much solve the
problem, but in general, this is an unwise thing to do.

Specifically, it is usually a bad idea to have a type class that should
have an instance for a type whenever that type has instances of some
combination of other classes.

Three ways around it:

   - Use a newtype wrapper: define `newtype LowerUpperBounded` and have the
   instance be `(LowerBounded a, UpperBounded a) => Bounded (LowerUpperBounded
   a)`
   - Defer instance definition to concrete types: if you know that a
   specific type has both super-instances, you can explicitly instantiate it
   at the sub-instance
   - Define a constraint alias: (this requires some more advanced
   extensions, so I'm only mentioning it as an option for completeness' sake)
   Using `ConstraintKinds`, you can define `type Bounded a = (LowerBounded a,
   UpperBounded a)`; this makes the two definitions synonymous.

To your immediate question, I *think* what's happening is that when you're
trying to do `minBound :: Bar` it looks for an instance `Bounded Bar` and
finds the unique one; then it needs to satisfy the constraints, so it looks
for `LowerBounded Bar` and `UpperBounded Bar`, the latter of which has two
possible instances, neither of which is marked overlapping or incoherent.

You'll notice that if you use `-fdefer-type-errors` you will be able to get
the `minBound` of `Bar` (though for some reason you need to bind it to a
variable), but not the `maxBound`.
You should also note that if you use the `OVERLAPPABLE` pragma rather than
the `INCOHERENT` one, you get the same results, and that is generally
considered less risky.

On Sun, Jun 3, 2018 at 8:43 PM Graham Gill <math.simplex at gmail.com> wrote:

> Please see the paste: https://pastebin.com/zBim7Zkx
>
> I'm experimenting with defining UpperBounded and LowerBounded typeclasses.
> An example type belonging to the latter that is not also Bounded would be
> type Natural from Numeric.Natural.
>
> I want to say that if a type is Bounded, then it is also UpperBounded and
> LowerBounded. If a type is both UpperBounded and LowerBounded, then it is
> also Bounded.
>
> To express the constraints, I need FlexibleInstances and
> UndecidableInstances extensions. These allow the module to load into ghci
> (8.4.2) with only a warning, but, without the INCOHERENT pragmas, I get an
> overlapping instance error if I try to evaluate minBound, maxBound,
> upperBound or lowerBound instantiated to either of the types Foo or Bar.
>
> A solution is to apply the INCOHERENT pragma to the instances at lines 11,
> 14 and 17. Reading over section 10.8.3.6. Overlapping instances in the GHC
> User Guide, I believe I understand. (Is there a better solution?)
>
> In the paste, I have INCOHERENT pragmas only at lines 11 and 17. This
> gives me the following behaviour in ghci:
>
>    1. minBound, maxBound, upperBound and lowerBound instantiated to type
>    Foo all function as expected, evaluating to the appropriate lower or upper
>    bound.
>    2. upperBound and maxBound instantiated at Bar give overlapping
>    instance errors for UpperBounded, as expected.
>    3. lowerBound :: Bar evaluates to C, as expected.
>    4. minBound :: Bar gives an overlapping instance error for
>    UpperBounded:
>
> *UpperLowerBounded> minBound :: Bar
>
> <interactive>:141:1: error:
>    • Overlapping instances for UpperBounded Bar
>        arising from a use of ‘minBound’
>      Matching instances:
>        instance [safe] Bounded a => UpperBounded a
>          -- Defined at UpperLowerBounded.hs:14:10
>        instance [safe] UpperBounded Bar -- Defined at
> UpperLowerBounded.hs:31:10
>    • In the expression: minBound :: Bar
>      In an equation for ‘it’: it = minBound :: Bar
>
>
> It's #4 that I don't understand. An explanation would be very much
> appreciated. (Also, what's a [safe] instance?)
>
> Regards,
> Graham
>
> _______________________________________________
> Beginners mailing list
> Beginners at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
>


_______________________________________________
Beginners mailing
listBeginners at haskell.orghttp://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/beginners/attachments/20180608/80357ce2/attachment.html>


More information about the Beginners mailing list