Ben373
Ben373

Reputation: 971

Haskell datatype range

I define my own datatype.

data Row = A | B | C deriving (Show,Eq,Ord)

The question is, if there is a more elegant way to define my range than this?

instance Ix Row where
  range (A,A) = [A]
  range (A,B) = [A,B]
  range (A,C) = [A,B,C]
  range (B,B) = [B]
  range (B,C) = [B,C]
  range (C,C) = [C]
  range _     = []

Upvotes: 5

Views: 1071

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477533

By using the Enum instance

Yes, you can make Row an instance of Enum, in fact you do not have to define it yourself:

data Row = A | B | C deriving (Show, Eq, Ord, Enum)

This means that we can now use "range notation". For example:

Prelude> [A .. C]
[A,B,C]
Prelude> [A .. B]
[A,B]
Prelude> [C .. B]
[]

So we can now define our Ix instance as:

instance Ix Row where
    range (x, z) = [x .. z]
    index (x, _) y = fromEnum y - fromEnum x
    inRange (x, z) y = x <= y && y <= z

Or even without parameters:

instance Ix Row where
    range = uncurry enumFromTo
    -- ...

By defining a custom "successor" function

In case you do not want to make it an Enum instance (I do not see any reason here not to, but this is of course a rather hypothetical datatype). We can construct a list by using recursion and each time calculating the successor:

instance Ix Row where
    range (x, z) = go x
        where go y | y > z = []
              go A = A : go B
              go B = B : go C
              go C = [C]

Although the code fragment above is still a large amount of work, it scales linear with the number of data constructors, whereas an exhaustive approach, would scale quadratic with the number of data constructors (not in time complexity, but in the amount of code).

Upvotes: 11

melpomene
melpomene

Reputation: 85877

There is: Derive Enum and define

range (x, y) = [x .. y]

Upvotes: 12

Related Questions