user4861515
user4861515

Reputation:

What should be my expectations for Haskell's "deriving"?

Apologies in advance for a beginner question, but I have struggled to find useful info on this. I was working through "Learn You Haskell for Great Good" and am trying to understand the deriving keyword, which seems like Java's implements but supposedly with cool automatic code generation because of the category theory stuff or something. I declare a data structure for 2-vectors like

data R2 = R2 {x :: Double, y :: Double} deriving (Show)

Then I can use it for things like

show (R2 1.0 2.0)

Now what I'd like to do is vector addition and scalar multiplication, like

(2.0 * (R2 1.0 2.0)) + (R2 3.0 4.0)

but when I try

Prelude> data R2 = R2 { x :: Double, y :: Double} deriving (Num,Show)
     <interactive>:3:52:
     Can't make a derived instance of `Num R2':
         `Num' is not a derivable class
     In the data declaration for `R2'

So the compiler figured out how to show the cartesian product of primitive types, but addition is too hard? Maybe Num isn't the right type class to derive? How often can one expect to derive a type class and get working code without additional work, like how I didn't have to write my own show function?

Thanks very much,

John

Upvotes: 8

Views: 334

Answers (5)

duplode
duplode

Reputation: 34398

trying to understand the deriving keyword, which seems like Java's implements but supposedly with cool automatic code generation

instance is a bit more like implements, in that you state that a type is an instance of a type class and then write the implementations. deriving is all about the cool automatic generation of those implementations (though it does subsume instance).

How often can one expect to derive a type class and get working code without additional work, like how I didn't have to write my own show function?

Alexey Romanov's answer covers for which classes deriving works. There is also another way to auto-generate instances: using generics. From a bird's eye view, it works like this: you describe what an instance should look like for a generic type and then, for any type you want to have an instance, derive Generic and add an empty (i.e. with no implementations, as they will be generated automatically) instance declaration. Some libraries like aeson and binary offer generic instances ready to use, and you can of course roll your own for your classes.

Upvotes: 6

MathematicalOrchid
MathematicalOrchid

Reputation: 62848

Sadly, deriving only works for a small handful of classes, where the necessary code is hard-wired into the compiler. You can write instances yourself for any class, but only this small handful can be derived automatically.

Upvotes: 0

Chris Taylor
Chris Taylor

Reputation: 47392

Here's one reason that you can't derive the Num class

data Vector = Vector Int Int

instance Num Vector where
    Vector a b + Vector c d = Vector (a + c) (b + d)
    Vector a b * Vector c d = Vector (a * c) (b * d)

data Complex = Complex Int Int

instance Num Complex where
    Complex a b + Complex c d = Complex (a + c) (b + d)
    Complex a b * Complex c d = Complex (a * c - b * d) (a * d + b * c)

Both are sensible instances that a real programmer might want to define. For a given data definition with two Int fields, which instance should the deriving clause pick?

Upvotes: 5

bennofs
bennofs

Reputation: 11973

The Haskell Report 2010 (the document that describes the Haskell language and which all implementions should follow) defines the conditions for deriving a class C as follows:

  1. C is one of Eq, Ord, Enum, Bounded, Show, or Read.
  2. There is a context cx′ such that cx′ ⇒ C tij holds for each of the constituent types tij.
  3. If C is Bounded, the type must be either an enumeration (all constructors must be nullary) or have only one constructor.
  4. If C is Enum, the type must be an enumeration.
  5. There must be no explicit instance declaration elsewhere in the program that makes T u1 … uk an instance of C.
  6. If the data declaration has no constructors (i.e. when n = 0), then no classes are derivable.

Also, later in the report, it is said that it's also possible to derive Data.Ix instances.

To find out how a particular instance is derived exactly (for example, what does the derived Show instance output?), read the section about it in the report. That section only gives implementations for the cases where the above conditions are met. That's why it's impossible to derive Num instances: it's not specified what that instance should do!


GHC also provides a few extensions that make allow for deriving more classes. Those extensions are not part of standard Haskell, so they have to enabled explicitly. For example, if GenericNewtypeDeriving is enabled, you can write the following:

newtype MyInt = MyInt Int deriving (Num)
-- By GenericNewtypeDeriving, GHC will just "copy" the instance 
-- for the base type of the newtype, in this case, it'll use the `Num Int` instance. 

You can read about these extensions in the GHC user guide

Upvotes: 4

Alexey Romanov
Alexey Romanov

Reputation: 170805

See https://downloads.haskell.org/~ghc/7.10.2/docs/html/users_guide/deriving.html:

In Haskell 98, the only classes that may appear in the deriving clause are the standard classes Eq, Ord, Enum, Ix, Bounded, Read, and Show.

GHC also allows deriving Generic, Functor, Data, Typeable, Foldable and Traversable for data declarations, and any classes for newtype declarations (after enabling the relevant extensions, as listed in the linked page).

Upvotes: 5

Related Questions