4

I have defined a Fine-Grained Functor class (FgFunctor) in order to apply a constraint to the type of functions that may map over my Ordered Triple datatype (OrdTriple), which requires the contained type be orderable.

import Data.List (sort)

-- 'fine-grained' functor
class FgFunctor f a b where
    fgmap :: (a -> b) -> f a -> f b

data OrdTriple a = OrdTriple a a a deriving Show

instance (Ord a, Ord b) => FgFunctor OrdTriple a b where
    fgmap f (OrdTriple n d x) = OrdTriple n' d' x'
        where [n', d', x'] = sort [f n, f d, f x]

main :: IO ()
main = do
    let test = fgmap (* 10^4) $ OrdTriple 1 6 11 
    print test

The code works fine until I define all other Functors to also be FgFunctors, like so:

-- all regular functors are also fine-grained ones
instance Functor f => FgFunctor f a b where
    fgmap = fmap

With that instance declaration, as soon as I try to use fgmap on my OrdTriple type, the compiler complains about overlapping instance declarations

Overlapping instances for FgFunctor OrdTriple b0 b0
  arising from a use of ‘fgmap’
Matching instances:
  instance Functor f => FgFunctor f a b
    -- Defined at OrdTriple.hs:15:10
  instance (Ord a, Ord b) => FgFunctor OrdTriple a b
    -- Defined at OrdTriple.hs:18:10
In the expression: fgmap (* 10 ^ 4)
In the expression: fgmap (* 10 ^ 4) $ OrdTriple 1 6 11
In an equation for ‘test’:
    test = fgmap (* 10 ^ 4) $ OrdTriple 1 6 11

However, the first 'matching instance' it refers to should never apply to OrdTriple, as OrdTriple is not a Functor, and so I am struggling to determine what is causing the supposed overlap.

Community
  • 1
  • 1
Zoey Hewll
  • 4,788
  • 2
  • 20
  • 33
  • See also related work. The simplest, in a way most hands-on approach is [`MonoFunctor`](http://hackage.haskell.org/package/mono-traversable-1.0.2/docs/Data-MonoTraversable.html#t:MonoFunctor), which does not derive any general instance. Functor classes in generalised/constrained category-theory libraries, like my [`Constrained.Functor`](http://hackage.haskell.org/package/constrained-categories-0.3.0.1/docs/Control-Functor-Constrained.html#t:Functor), solve the problem by giving the prelude-functor derived instance unambiguous tags (the category to work in). – leftaroundabout Mar 20 '17 at 12:41
  • Is my understanding correct that a `MonoTraversible` type, when mapped over, always returns a `MonoTraversible` of the same type? So `(omap f (xs :: t)) :: t` for all legal combinations of `f` and `x`? – Zoey Hewll Mar 20 '17 at 13:49
  • Yes, that class is indeed quite limiting. – leftaroundabout Mar 20 '17 at 15:04

1 Answers1

5

When the compiler (GHC) looks for instances of a type class for a specific type, it doesn't take contexts of each instance into account. That's why it found two instances of FgFunctor for OrdTriple even though OrdTriple is not an instance of Functor.

GHC/AdvancedOverlap shows you how you can solve this situation.

snak
  • 6,483
  • 3
  • 23
  • 33