How to list all values of sum type in Haskell

134 Views Asked by At

Question:

I would like to generate a HTML form from types in Haskell.

Color = Green | Yellow | Red | ....

Fruit = Apple Color Int
      | Banana Color Int String
      | ....

User shall pick a dropdown list with option of ("Apple","Banana") or ("Green","Yellow","Red")

Question:

But how to get ALL values of type Fruit/Color values to HTML ? something like:

[ toHtml v <-  Fruit.values() ]
-- render "Apple" to html
-- render "Banana" to html

expected:

<dropdown> : values :apple/banana...
<dropdown> : values of green/yellow...
<input text> : for Int
<input text> : for String(if Banana was selected
3

There are 3 best solutions below

0
Cigarette Smoking Man On BEST ANSWER

The Data class helps here, e.g

data Color = Green | Yellow | Red deriving Data

colorStrings :: [String]
colorStrings = map show $ dataTypeConstrs (dataTypeOf Green :: DataType)

To get Fruit where data constructors also have fields you'd have to work harder but the required info should be available within the DataType data type.

1
Geoffrey Warne On

I have to make several assumptions to answer the question. At first I thought you were looking for all possible values of Fruit, but obviously this is not practical because there is an infinite number with combinations of Int and String. I am also assuming that you are not trying to automatically generate the dropdown menu, because you do not say anything about getting values for Int or String. So I am assuming that the users is generating a list of fruit records, perhaps an inventory of a produce section. So when you get to displaying the inventory you have a list of values already created.

Here I have created a short list of values and implemented a list comprehesion to convert them to Html strings.

data Color = Green
           | Yellow
           | Red
             deriving Show
    
data Fruit = Apple Color Int
           | Banana Color Int String
             deriving Show

type Html = String

values :: [Fruit]
values = [Apple Red 5, Apple Green 4, Apple Yellow 3,
         Banana Green 3 "Plantain", Banana Green 2 "Regular",
         Banana Yellow 23 "Regular", Banana Red 6 "Fancy"]

toHtml :: Fruit -> Html
toHtml (Apple color number) =
       "<fruit=Apple" ++ ", color=" ++ show color ++
       ", number=" ++ show number ++ ">"
toHtml (Banana color number label) =
       "<fruit=Banana" ++ ", color=" ++ show color ++
       ", number=" ++ show number ++ ", label=" ++ label ++ ">"

showAll = [toHtml v | v <- values]

I hope this answers your question.

1
Geoffrey Warne On

How about something like this:

import Data.Char (toLower)

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

data Fruit = Apple
           | Banana 
             deriving (Show, Eq, Ord, Bounded, Enum)

type Html = String


type Choice = (Fruit,Color)

listOfFruits :: [Choice]
listOfFruits = [(fruit, color) | fruit <- [Apple ..],
                                 color <- [Green ..]]

toDropDown :: [Choice] -> Html
toDropDown choices =
  "<label for=\"fruit_choices\">Choose a fruit:</label>\n"
  ++ "    <select name=\"fruit-choices\" id=\"fruit-choices\">\n"
  ++ toOptionList choices
  ++ "    </select>"


toOptionList :: [Choice] -> Html
toOptionList choices = concatMap toOptionHtml choices

toOptionHtml :: Choice -> Html
toOptionHtml choice =
      "        <option value=\"" ++ tagValue choice ++ "\">"
                                 ++ listText choice
                                 ++ "</option>\n"
  where tagValue (fruit,color) = map toLower (show fruit) ++ "/" ++
                                 map toLower (show color)           -- "apple/red"
        listText (fruit,color) = show color ++ " " ++ show fruit    -- "Red Apple"

This produces the output:

*Main> putStrLn (toDropDown listOfFruits)
<label for="fruit_choices">Choose a fruit:</label>
    <select name="fruit-choices" id="fruit-choices">
        <option value="apple/green">Green Apple</option>
        <option value="apple/yellow">Yellow Apple</option>
        <option value="apple/red">Red Apple</option>
        <option value="banana/green">Green Banana</option>
        <option value="banana/yellow">Yellow Banana</option>
        <option value="banana/red">Red Banana</option>
    </select>

My html is very rusty. I hope this is close.

Getting closer?

From a Haskell perspective, the tricky part is getting combinations of Fruit and Color using a list-comprehension and the fromEnum syntactic sugar of [Green ..]. To use the range syntax, Fruit and Color have to be Enum and Bounded. These type-classes are easily derived.

I leave off the Int and String elements because I do not understand that part of the problem still.