Declaring a "subclass" in Haskell

I am having problems 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 throws different kinds of errors depending on the flag combinations.

I know it is Num a

not shorter ICycle a

and hence the compiler cannot complete the type check. I've seen in the examples that this is bypassed either by the right side having a larger term, or by declaring the class of interests as a subclass of other classes. Here, on the contrary, I essentially want to declare the existing class as a subclass of the new one.

I wonder if there is any objection to this kind of use of typeclasses. Or if there is a natural solution.

+3


source to share


2 answers


For this specific example, I think you are better off using newtype

to wrap the instance:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Prelude hiding (cycle)

class ICycle a where
    cycle :: a -> a

newtype Succ a = Succ { runSucc :: a }
  deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)

newtype Pred a = Pred { runPred :: a }
  deriving (Num, Eq, Ord, Bounded, Enum, Show, Read)

instance Enum a => ICycle (Succ a) where
    cycle = Succ . succ . runSucc

instance Enum a => ICycle (Pred a) where
    cycle = Pred . pred . runPred

main = do
    print $ cycle $ (42 :: Succ Int)
    print $ cycle $ (42 :: Pred Int)

      

There are several ways that one could cycle through the numbers - by succ, by pred, by doubling, twice. The advantage of using newtype

for instance (which makes RHS "bigger" as you noted in your question) is that it allows us to have ALL of them.



The standard library does the same trick with Product

and Sum

for Monoid

.

Look at it in a different way, if it was possible to define a new superclass for Num

by adding a default implementation for all instances Num

, then you would take that choice away from all of those implementations. It might not make sense.

+3


source


According to the Haskell 2010 report, Chapter 4, Declarations and Bindings , the subject to be defined as an instance must be a constructor type. Thus,

instance Num a => ICycle a where
    ...

      

is invalid because it a

is a type variable and not a type constructor.



So the valid way to do this is unfortunately by type. Need to say:

instance ICycle Int where
    ...

instance ICycle Double where
    ...

      

etc.

+1


source







All Articles