How should types be used in Haskell type classes?

haskell, typeclass

Solution

You're really seeing two separate problems, so I'll address them as such.

The first one is with the `value` field. Haskell records work in a slightly peculiar way: when you name a field, it is automatically added to the current scope as a function. Essentially, you can think of

data ListOfInts = ListOfInts {value :: [Int]}

as syntax sugar for:

data ListOfInts = ListOfInts [Int]

value :: ListOfInt -> [Int]
value (ListOfInts v) = v

So having two records with the same field name is just like having two different functions with the same name--they overlap. This is why your first error tells you that you've declared `values` multiple times.

The way to fix this would be to define your types without using the record syntax, as I did above:

data ListOfInts = ListOfInts [Int]
data ListOfDoubles = ListOfDoubles [Double]

When you used `type` instead of `data`, you simply created a type synonym rather than a new type. Using

type ListOfInts = [Int]

means that `ListOfInts` is the same as just `[Int]`. For various reasons, you can't use type synonyms in class instances by default. This makes sense--it would be very easy to make a mistake like trying to write an instance for `[Int]` as well as one for `ListOfInts`, which would break.

Using `data` to wrap a single type like `[Int]` or `[Double]` is the same as using `newtype`. However, `newtype` has the advantage that it carries no runtime overhead at all. So the best way to write these types would indeed be with `newtype`:

newtype ListOfInts = ListOfInts [Int]
newtype ListOfDoubles = ListOfDoubles [Double]

An important thing to note is that when you use `data` or `newtype`, you also have to "unwrap" the type if you want to get at its content. You can do this with pattern matching:

instance Incrementable ListOfInts where
  increment (ListOfInts ls) = ListOfInts (map (\ x -> x + 1) ls)

This unwraps the `ListOfInts`, maps a function over its contents and wraps it back up.

As long as you unwrap the value this way, your instances should work.

On a side note, you can write `map (\ x -> x + 1)` as `map (+ 1)`, using something that is called an "operator section". All this means is that you implicitly create a lambda filling in whichever argument of the operator is missing. Most people find the `map (+ 1)` version easier to read because there is less unnecessary noise.

Problem

I'm new to Haskell, and a little confused about how type classes work. Here's a simplified example of something I'm trying to do: ``` data ListOfInts = ListOfInts {value :: [Int]} data ListOfDoubles = ListOfDoubles {value :: [Double]} class Incrementable a where increment :: a -> a instance Incrementable ListOfInts where increment ints = map (\x -> x + 1) ints instance Incrementable ListOfDoubles where increment doubles = map (\x -> x + 1) doubles ``` (I realize that incrementing each element of a list can be done very simply, but this is just a simplified version of a more complex problem.) The compiler tells me that I have multiple declarations of `value`. If I change the definitions of `ListOfInts` and `ListOfDoubles` as follows: ``` type ListOfInts = [Int] type ListOfDoubles = [Double] ``` Then the compiler says "Illegal instance declaration for 'Incrementable ListOfInts'" (and similarly for `ListOfDoubles`. If I use newtype, e.g., `newtype ListOfInts = ListOfInts [Int]`, then the compiler tells me "Couldn't match expected type 'ListOfInts' with actual type '[b0]'" (and similarly for `ListOfDoubles`. My understanding of type classes is that they facilitate polymorphism, but I'm clearly missing something. In the first example above, does the compiler just see that the type parameter `a` refers to a record with a field called `value` and that it appears I'm trying to define `increment` for this type in multiple ways (rather than seeing two different types, one which has a field whose type of a list of `Int`s, and the other whose type is a list of `Double`s)? And similarly for the other attempts? Thanks in advance.

Original source