Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can GHC warn if class instance is a loop?

Tags:

haskell

Real World Haskell has this example:

class BasicEq3 a where
    isEqual3 :: a -> a -> Bool
    isEqual3 x y = not (isNotEqual3 x y)

    isNotEqual3 :: a -> a -> Bool
    isNotEqual3 x y = not (isEqual3 x y) 

instance BasicEq3 Bool

And when I run it in GHCI:

#> isEqual3 False False
out of memory

So, you have to implement at least one of the 2 methods or it will loop. And you get the flexibility of choosing which one, which is neat.

The question I have is, is there a way to get a warning or something if didn't override enough of the defaults and the defaults form a loop? It seems strange to me that the compiler that is so crazy smart is fine with this example.

like image 951
Adam Gordon Bell Avatar asked Sep 04 '12 19:09

Adam Gordon Bell


2 Answers

I think it's perfectly fine for GHC to issue a warning in case of an "unbroken" cyclic dependency. There's even a ticket along those lines: http://hackage.haskell.org/trac/ghc/ticket/6028

Just because something is "undecidable" doesn't mean no instance of the problem can be solved effectively. GHC (or any other Haskell compiler) already has quite a bit of the information it needs, and it'd be perfectly possible for it to issue a warning if the user is instantiating a class without "breaking" the cyclic dependency. And if the compiler gets it wrong in the rare cases as exemplified in previous posts, then the user can have a -nowarnundefinedcyclicmethods or a similar mechanism to tell GHC to be quiet. In nearly every other case, the warning will be most welcome and would add to programmer productivity; avoiding what's almost always a silly bug.

like image 92
alias Avatar answered Nov 04 '22 02:11

alias


No, I'm afraid GHC doesn't do anything like that. Also that isn't possible in general.

You see, the methods of a type class could be mutually recursive in a useful way. Here's a contrived example of such a type class. It's perfectly fine to not define either sumOdds or sumEvens, even though their default implementations are in terms of each other.

class Weird a where
    measure :: a -> Int

    sumOdds :: [a] -> Int
    sumOdds [] = 0
    sumOdds (_:xs) = sumEvens xs

    sumEvens :: [a] -> Int
    sumEvens [] = 0
    sumEvens (x:xs) = measure x + sumOdds xs
like image 12
opqdonut Avatar answered Nov 04 '22 01:11

opqdonut