Mixed Drink Calculator in Haskell
Good News:

Architecturally, your code looks very well designed. I can't think of any language features that would significantly improve your code's architecture. That leaves only function semantics and readability.

Advise:

This advise isn't specific to idiomatic Haskell but rather general code readability.

Your current main method does a little too much. the contents of main should have a high level of abstraction. Consider abstracting the work done at the end of your main function:

main = do drinks      &lt;- readFile "drinks"
          ingredients &lt;- readFile "ingredients"
          putStrLn $ 
              case parse drinksFile "drinksFile" drinks of
              Left error         -&gt; "Error parsing drinks file."
              Right parsedDrinks -&gt; showPossibleDrinks parsedDrinks (lines ingredients)

showPossibleDrinks :: [Drink] -&gt; [Ingredient] -&gt; String
showPossibleDrinks drinks ingredients = showDrinks $ filterByIngredients drinks ingredients

showDrinks :: [Drink] -&gt; String
showDrinks =  unlines . map printDrink


Here we have abstracted the process of printing the possible drinks into two sub methods. This provides greater readability and also allows us to change the return type of case from IO to String. Hence the putStrLn outside the case statement.



By changing the argument order of a few functions, we can  move the functions towards pointfree form:

canMake :: [Ingredient] -&gt; Drink -&gt; Bool
canMake ingredients = all (flip elem ingredients) . snd

filterByIngredients :: [Ingredient] -&gt; [Drink] -&gt; [Drink]
filterByIngredients ingredients = filter (canMake ingredients)

main = do drinks      &lt;- readFile "drinks"
          ingredients &lt;- readFile "ingredients"
          putStrLn $
              case parse drinksFile "drinksFile" drinks of
              Left error         -&gt; "Error parsing drinks file."
              Right parsedDrinks -&gt; showPossibleDrinks (lines ingredients) parsedDrinks

showPossibleDrinks :: [Ingredient] -&gt; [Drink] -&gt; String
showPossibleDrinks = showDrinks . (filterByIngredients ingredients)


Given the succinctness of your methods, moving towards pointfree form is quite readable and more idiomatic.



If we import the &lt;$&gt; operator from Control.Applicative after transposing the function's arguments we can make filterByIngredients completely pointfree:

filterByIngredients :: [Ingredient] -&gt; [Drink] -&gt; [Drink]
filterByIngredients =  filter &lt;$&gt; canMake




You can make the following equivalence substitutions:

(flip elem ingredients) ==&gt; (`elem` ingredients)
intercalate " " ==&gt; unwords


Giving you:

canMake :: [Ingredient] -&gt; Drink -&gt; Bool
canMake ingredients = all (`elem` ingredients) . snd

printDrink :: Drink -&gt; String
printDrink (drinkName, recipe) = unwords (map capitalize (words drinkName)) ++
                                ": " ++
                                intercalate ", " (map capitalize recipe)
                                where capitalize (x : xs) = toUpper x : xs




All together we have:

import Control.Applicative ((&lt;$&gt;))
import Data.Char
import Data.List
import Text.ParserCombinators.Parsec

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 &lt;- drinkName
            next  &lt;- recipe
            return (first, next)

-- parsing the drink name
drinkName :: GenParser Char st DrinkName
drinkName =  do name &lt;- many (noneOf ":")
                sep  &lt;- many (char ':')
                return name

-- parsing the recipe into a list of ingredients
recipe :: GenParser Char st [Ingredient]
recipe =  sepBy (many (noneOf ",\n")) (char ',')

canMake :: [Ingredient] -&gt; Drink -&gt; Bool
canMake ingredients = all (`elem` ingredients) . snd

filterbyIngredients :: [Ingredient] -&gt; [Drink] -&gt; [Drink]
filterbyIngredients = filter &lt;$&gt; canMake

-- pretty prints a drink with its recipe
printDrink :: Drink -&gt; String
printDrink (drinkName, recipe) = unwords (map capitalize (words drinkName)) ++
                                ": " ++
                                intercalate ", " (map capitalize recipe)
                                where capitalize (x : xs) = toUpper x : xs

showPossibleDrinks :: [Ingredient] -&gt; [Drink] -&gt; String
showPossibleDrinks ingredients = showDrinks . filterbyIngredients ingredients

showDrinks :: [Drink] -&gt; String
showDrinks =  unlines . map printDrink

main = do drinks      &lt;- readFile "drinks"
          ingredients &lt;- readFile "ingredients"
          putStrLn $ 
              case parse drinksFile "drinksFile" drinks of
              Left error         -&gt; "Error parsing drinks file."
              Right parsedDrinks -&gt; showPossibleDrinks (lines ingredients) parsedDrinks