This is a simple mixed drink calculator written in Haskell.
There are two input files. The
drinks file contains a list of simply formatted recipes for mixed drinks:
screwdriver:vodka,orange juice white russian:vodka,coffee liqueur,cream greyhound:gin,grapefruit juice ...
ingredients file contains a line-separated list of presently available ingredients:
vodka gin rum orange juice cranberry juice grapefruit juice ...
The calculator parses the drinks file and the ingredients file, determines which mixed drinks can be created with the available ingredients, and pretty-prints the results to screen:
Screwdriver: Vodka, Orange juice Greyhound: Gin, Grapefruit juice ...
It's a simple program, but I want to improve my code style and make my Haskell more idiomatic.
import Text.ParserCombinators.Parsec import Data.Char import Data.List type Drink = (DrinkName, [Ingredient]) type DrinkName = String type Ingredient = String -- parsing the file of drink recipes drinksFile ∷ GenParser Char st [Drink] drinksFile = endBy drink (char '\n') -- parsing each drink recipe drink ∷ GenParser Char st Drink drink = do first ← drinkName next ← recipe return (first, next) -- parsing the drink name drinkName ∷ GenParser Char st DrinkName drinkName = do name ← many (noneOf ":") sep ← many (char ':') return name -- parsing the recipe into a list of ingredients recipe ∷ GenParser Char st [Ingredient] recipe = sepBy (many (noneOf ",\n")) (char ',') -- returns true iff the drink can be made with the given ingredients canMake ∷ [Ingredient] → Drink → Bool canMake ingredients drink = all (flip elem ingredients) recipe where recipe = snd drink -- returns the drinks that can be made with the given ingredients filterByIngredients ∷ [Drink] → [Ingredient] → [Drink] filterByIngredients drinks ingredients = filter (canMake ingredients) drinks -- pretty prints a drink with its recipe printDrink ∷ Drink → String printDrink (drinkName, recipe) = intercalate " " (map capitalize (words drinkName)) ++ ": " ++ intercalate ", " (map capitalize recipe) where capitalize (x : xs) = toUpper x : xs main = do drinks ← readFile "drinks" ingredients ← readFile "ingredients" case parse drinksFile "drinksFile" drinks of Left error → do putStrLn "Error parsing drinks file." Right parsedDrinks → mapM_ (putStrLn ∘ printDrink) (filterByIngredients parsedDrinks (lines ingredients))