47

If I have a data type say:

data Color = Red | Yellow | Green

Is there a way I can turn this into a list of type [Color] getting all possible values? [Red, Yellow, Green]

Perhaps this is a complete anti pattern?

1
  • I believe [toEnum 0 ..] works no matter how you change the data definition. Commented Jul 16, 2017 at 0:34

4 Answers 4

64

Not sure if it is an anti-pattern (nor can I think of a good use right now), but it's possible. Use the Enum (allows to generate a list like [someCtor .. someOtherCtor]) and Bounded (for minBound and maxBound) type classes. Luckily, you can derive both:

data Color = Red
           | Yellow
           | Green
           deriving (Enum, Bounded)

allColors = [(minBound :: Color) ..]

If you ever add another color, allColors get updated automatically. One restriction though: Enum requires all contructors to be nullary, i.e. adding Foo Int breaks the whole thing. Luckily, because a list of all possible values for this would be way too large.

Edit: The other answer works as well, maybe better since it doesn't require deriving Bounded and is therefore a bit shorter. I'll still leave mine because I love over-engineered but extremely generic code ;)

Sign up to request clarification or add additional context in comments.

1 Comment

46

Surely delnan's answer is better. Since I do not know how to include a piece of code in a comment, I'll give a generalisation as a separate answer here.

allValues :: (Bounded a, Enum a) => [a]
allValues = [minBound..]

Now, this works for any type with a Bounded and Enum instance! And allColors is just a special case:

allColors :: [Color]
allColors = allValues

In many cases, you won't even need to define allColors separately.

2 Comments

I agree with you, since Delnan's solutions doesn't require any reference to a real value from the type. On a SO note, perhaps I accepted too quickly, but the answer by adamse did work exactly as I asked.
+1 good point. (You can use backticks as usual in comments, linebreaks will be removed though - also, this wouldn't fit into a comment and you deserve rep for it anyway :) )
37
data Color = Red
           | Yellow
           | Green
           deriving Enum

allColors = [Red ..]

2 Comments

This will break down when we add | Pink.
I believe [toEnum 0 ..] works no matter how you change the data definition.
4

Here is an example of using this technique to parse enums with Parsec

data FavoriteColor = Maroon | Black | Green  | Red | 
                     Blue   | Pink  | Yellow | Orange
                             deriving (Show, Read, Enum, Bounded)

And the parsec parser

parseColor :: Parser FavoriteColor           
parseColor = fmap read . foldr1 (<|>) $ map (try . string . show) 
  [ minBound :: FavoriteColor ..] 

of course the try could could be applied by better by pattern matching and a few other things could make it nicer but this is just an example of some usage of the technique.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.