I have troubles with the following simple code in Haskell:
import Prelude hiding (cycle).
class ICycle a where
cycle :: a -> a
instance ICycle [a] where
cycle [] = []
cycle (x:xs) = xs ++ [x]
instance ICycle Bool where
cycle True = False
cycle False = True
instance Num a => ICycle a where
cycle n = n+1
main = do
print $ cycle $ [1,2,3]
print $ cycle $ True
print $ cycle $ 42
Here the first two instance declarations work as expected, but the third one triggers various sorts of errors depending on flag combinations.
I know that Num a
is no shorter than ICycle a
and hence the compiler can not finish type checking. In the examples, I have seen this is circumvented by either making the right-hand side a bigger term or by declaring a class of interest a subclass of other classes in the first place. Here, to the contrary, I essentially want to declare an existing class to be a subclass of a new one.
I wonder if there are objections against this kind of use of type classes. Or else, if there is a natural solution.
For this particular example, I think you're best off using a
newtype
to wrap the instance:There are multiple ways one could cycle through numbers - by succ, by pred, by doubling, by halving. The advantage of using a
newtype
for the instance (making the RHS "bigger", as you noted in your question) is that it lets us have ALL of them.The standard library does the same trick with
Product
andSum
forMonoid
.Looking at it another way, if it were possible to define a new superclass for
Num
, adding a default implementation for all instances ofNum
, then you'd be taking that choice away from all those implementations. In possibly a way that doesn't make sense.