4

I have some code like this:

{-# OPTIONS_GHC -Wall #-}
{-# LANUAGE VariousLanguageExtensionsNoneOfWhichWorked #-}

import Control.Applicative
import Data.Either
import Data.Void

class Constructive a where
    lem :: Either (a -> Void) a

instance Constructive Void where
    lem = Left id

instance Num a => Constructive a where
    lem = Right 0

instance Enum a => Constructive a where
    lem = Right $ toEnum 0

instance Bounded a => Constructive a where
    lem = Right minBound

instance Monoid a => Constructive a where
    lem = Right mempty

instance Alternative f => Constructive (f a) where
    lem = Right empty

The problem is, GHC complains with

pad.hs:49:10:
    Duplicate instance declarations:
      instance [overlap ok] Bounded a => Constructive a
        -- Defined at pad.hs:49:10
      instance [overlap ok] Monoid a => Constructive a
        -- Defined at pad.hs:52:10

Along with a bunch of similar errors.

Is there a way to tell GHC to pick one at random, since I don't care which it uses? (I don't even care if it picks a different one each time I use lem, since it does not matter.)

PyRulez
  • 10,513
  • 10
  • 42
  • 87
  • 1
    Have you tried splitting the instances up into more (non-overlapping) modules? – Nathan Howell Dec 25 '15 at 01:10
  • 5
    GHC can not simply choose any arbitrary instance, since their contexts might be not satisfied. To pick one, GHC would need to do backtracking, but it is designed not to do that. – chi Dec 25 '15 at 08:27
  • 1
    All of those overlapping instances fulfill one purpose - construct some default value for some type. You do not use the mappend of Monoid or the succ of Enum, for example, so using those constraints here is certainly wrong (semantically). Fortunately there is a [library](https://hackage.haskell.org/package/data-default-0.5.3/docs/Data-Default.html) which captures the "default value" relation in a typeclass called `Default`, allowing you to write one instance: `instance Default a => Constructive a where lem = Right default`. – user2407038 Dec 26 '15 at 16:42

1 Answers1

2

This is not really an answer to your question, more like an extended comment suggesting another route how to tackle the problem.

In Haskell the canonical solution would be to create a newtype for each of your instances, which is probably not what you want. However, I'd like to suggest you an alternative approach.

In Haskell we basically have 3 possibilities how to construct a data type:

  1. Algebraic data types using products and coproducts (disjoint unions).
  2. Function types.
  3. Primitive types.

For the first part, we could use SYB or GHC Generics. If a product is empty, or has an empty factor, it maps to a -> Void. And a coproduct maps to a -> Void iff all its summands do.

A function type a -> b is constructive if both a and b are:

instance (Constructive a, Constructive b) => Constructive (a -> b) where
  ...

If x :: b is nonempty, a -> b is inhabited by const x. If a is empty then a -> b is inhabited by absurd. And if a is non-empty and b is empty, a -> b maps to Void.

All Haskell primitive types are non-empty, so they're trivially constructive.

Unfortunately it seems there is no way to tell GHC that all data types are one of these three. My suggestion would be to implement the instance for -> and then either

  • Try to use SYB to implement an instance for everything that implements Data. There would still be the problem how to deal with overlapping instances. Or:
  • Try to use GHC Generics to provide default instances for ADTs and implement instances manually for primitive types. This would mean that for every data type you'd have to still provide an empty instance implementation, with the default provided by Generics.

After writing this, I discovered AdvancedOverlap. Perhaps combining it with one of the previous approaches could lead to a nice solution.

Petr
  • 62,528
  • 13
  • 153
  • 317