3

I have a question similar to a previous one, but where the superclass is Eq. For example, suppose, I have the following:

{-# LANGUAGE DefaultSignatures #-}
class (Eq a) => Foo a where
  size :: a -> Int
  (==) :: a -> a -> Bool
  (==) s t = (size s) == (size t)

(Note, that I've included the language extension as suggested in the solution to the aforementioned question)

I receive the following ghci error message:

Ambiguous occurrence ‘==’
It could refer to either ‘Main.==’,
                         defined at permutations.lhs:162:3
                      or ‘Prelude.==’,
                         imported from ‘Prelude’ at permutations.lhs:1:1
                         (and originally defined in ‘GHC.Classes’)

Am I trying to do something impossible in Haskell? I know that I could instead do something like

class (Eq a) => Foo a where
  size :: a -> Int

data Bar = Qux [Int]    

instance Foo Bar where
  size (Qux xs) = length xs

instance Eq Bar where
    (==) f g = (size f) == (size g)

but I'd then have to copy the definition of (==) for every instance of Foo, rather than making it a default definition.

I also realise that had I used by own superclass instead of Eq, I could have written

class Bam a where
  eqs :: a -> a -> Bool
  default eqs :: Roo a => a -> a -> Bool
  eqs f g = (size f) == (size g)

class (Bam a) => Roo a where
  size :: a -> Int

The issue I have is that the superclass is Eq, and that I don't want to repeat the same definition in each instance.

Community
  • 1
  • 1
gihan
  • 53
  • 6
  • 3
    `(==)` is a method of the `Eq` typeclass. It cannot simultaneously be in scope as a method of the `Foo` typeclass. This is conceptually the same as having `foo = 1` and `foo = 2` in the same scope. – Rein Henrichs Dec 24 '15 at 22:52
  • It seems like you just want `instance Foo a => Eq a where a == b = size a == size b`, but this is effectively useless because of problems with overlapping instances. – Rein Henrichs Dec 24 '15 at 22:57
  • Thanks for your response! It seems that I can do something like what I want for classes that I define (as I mention at the bottom of my question), but there are issues where the superclass is defined elsewhere. Maybe I'm asking for something impossible. – gihan Dec 24 '15 at 23:20
  • 1
    The standard trick is to offer `eqDefault :: Foo a => a -> a -> Bool` and let people write `instance Eq Whatever where (==) = eqDefault`. I describe this in [this answer](http://stackoverflow.com/a/34098924/791604). I'm tempted to mark this as a duplicate of that question -- any objections? – Daniel Wagner Dec 25 '15 at 05:06
  • @DanielWagner: One. Add `fmapDefault` and `foldMapDefault` as `base` examples. – Zeta Dec 25 '15 at 11:52
  • Thanks @DanielWagner for your suggestion. I'd hoped to avoid having explicitly to make each instance of Foo also an instance of Eq, but I take it that this is unavoidable! Please mark as duplicate if you wish. – gihan Dec 25 '15 at 22:22

1 Answers1

1

You can define functions (and operators) with the same name, as long as they are in different modules. So there is Prelude.== (default one), and yours (say, My.==). In the default definition of My.==, compiler doesn't know which to use. Fix is trivial yet ugly:

{-# LANGUAGE DefaultSignatures #-}
module My where

class (Eq a) => Foo a where
  size :: a -> Int
  (==) :: a -> a -> Bool
  (==) s t = (size s) Prelude.== (size t)

instance Eq a => Foo [a] where
  size = length

main :: IO ()
main = do
    print $ a Prelude.== b
    print $ a My.== b
  where
    a = [1, 2, 3]
    b = [3, 4, 5]

When run:

> main
False
True

Yet, i'd prefer to use some other operator name, for example ~=.

phadej
  • 11,947
  • 41
  • 78
  • 1
    You should probably mention that `DefaultSignatures` isn't necessary here, though it does no harm. – dfeuer Dec 25 '15 at 15:36
  • Thanks (and Merry Christmas)! I really wanted to have the *same* `(==)` as in the Prelude, but I didn't make that clear in my question. A previous commenter (DanielWagner) made a suggestion that works, but only by explicitly declaring each instance of `Foo` to be an instance of `Eq`. I imagine that this is a barrier I cannot work around. – gihan Dec 25 '15 at 22:32