Search code examples
haskellfunctional-programmingderiving

What does deriving do/mean in Haskell?


The definition of deriving on stack overflow is:

"In Haskell, a derived instance is an instance declaration that is generated automatically in conjunction with a data or newtype declaration. The body of a derived instance declaration is derived syntactically from the definition of the associated type."

I don't really understand any of it to be honest.

The code below is taken from: Link

data BaseballPlayer = Pitcher 
                        | Catcher
                        | Infielder
                        | Outfielder
                        deriving Show

barryBonds :: BaseballPlayer -> Bool
barryBonds Outfielder = True

barryInOf = print(barryBonds Outfielder)

My question is, what does the deriving-statement do in this specific case, and also what does the deriving-statement do in general?.


Solution

  • In short:

    deriving automatically implements functions for a few of Haskell's typeclasses such as Show and Eq. This cannot be done with arbitrary typeclasses, but the ones for which deriving does work for are simple enough for automatic implementation.

    The Show typeclass defines functions for how to represent data types as a String.

    More extensively:

    Are you familiar with typeclasses?

    https://www.haskell.org/tutorial/classes.html

    Typeclasses are similar to interfaces in Java: they define a few functions that any data type who wants to use those functions can implement.

    For instance, say we have a class like such:

    class Comparable a where
        lessThan :: a -> a -> Bool
        equalsTo :: a -> a -> Bool
    

    Beware of the word class. It means typeclass in the Haskell setting, not a typical "class" you would hear about in object oriented languages. The a here is a filler type, similar to how you would expect templates would work in C++ and generics to behave in Java.

    Let's say we define a data type as follows:

    data Color = Red | Green | Blue
    

    To make Comparable work with Color, we implement an instance of Comparable:

    instance Comparable Color where
        lessThan Red   Green = True
        lessThan Red   Blue  = True
        lessThan Green Blue  = True
        lessThan _     _     = False
    
        equalsTo Red   Red   = True
        equalsTo Green Green = True
        equalsTo Blue  Blue  = True
        equalsTo _     _     = False
    

    Roughly speaking, this now allows you to "compare" Red, Green, and Blue with each other. But was there any way that GHC could have automagically guessed that this is was the exact "order" you wanted?

    Taking a step back, the typeclass Show has a similar structure:

    https://hackage.haskell.org/package/base-4.9.1.0/docs/src/GHC.Show.html#Show

    class  Show a  where
        showsPrec :: Int -> a -> ShowS
        show      :: a   -> String
        showList  :: [a] -> ShowS
    
        showsPrec _ x s = show x ++ s
        show x          = shows x ""
        showList ls   s = showList__ shows ls s
    

    Something to notice is that functions within a typeclass may be defined in terms of each other. Indeed, we could have also easily done:

    class Comparable a where
        lessThan :: a -> a -> Bool
        equalsTo :: a -> a -> Bool
    
        greaterThan :: a -> a -> Bool
        greaterThan lhs rhs = not (lessThan lhs rhs && equalsTo lhs rhs)
    

    However, the key point is this: for arbitrary user-defined typeclasses, GHC has no idea how their functions should be implemented when you try to associate the typeclass with a data type such as Color or BaseballPlayer. For some typeclasses such as Show, Eq, Ord, etc, where the functionality is simple enough, GHC can generate default implementations that you can of course overwrite yourself.

    Indeed, let's experiment by trying to compile the following:

    data Color = Red | Green | Blue deriving (Comparable)
    

    The result I get is this:

    test.hs:9:43:
        Can't make a derived instance of ‘Comparable Color’:
          ‘Comparable’ is not a derivable class
          Try enabling DeriveAnyClass
        In the data declaration for ‘Color’
    

    Of course, some GHC extensions can be used to extend the power of deriving, but that's for another day :)