# Overly complicated parser combinator

I recently started to learn Haskell in order to write some small tools for me. Currently I'm working on a program which calculates how much steam a (railcraft) boiler of a given size outputs per tick.

In the console the user inputs a string of the following format: S | L | BL-Size | BH-Size, example BH-3x3x4

I then use ParserCombinators from the Text.ParserCombinators.ReadP library to parse this string into a custom type. However I think my parser is overly complicated. Any suggestions how to simplify the following code?

module Boilers (Boiler(..), Tank(..)) where

data Boiler = Hobbyist | Locomotive | Boiler (Int, Int, Int) Tank deriving (Show)
data Tank = HighPressure | LowPressure deriving (Eq, Show)

module BoilerParser where

import Data.Char
import Control.Applicative
import Boilers
import Data.Maybe

boiler = do
b <- standard <++ hobbyist <++ locomotive
maybe pfail return b

standard = do
satisfy (\c -> toUpper c == 'B')
t <- fmap (\t -> case toUpper t of
'H' -> Just HighPressure
'L' -> Just LowPressure
_   -> Nothing) get
satisfy (== '-')
s <- BoilerParser.size
case t of
Nothing -> pfail
(Just a) -> return (Just (Boiler s a))

hobbyist = fmap (\c -> if toUpper c == 'H' then Just Hobbyist else Nothing) get

locomotive = fmap (\c -> if toUpper c == 'L' then Just Locomotive else Nothing) get

size :: ReadP (Int, Int, Int)
size = do
x <- size' 1
satisfy (== 'x')
y <- size' 1
satisfy (== 'x')
z <- size' 1
return (x,y,z)


boiler :: ReadP Boiler
=   Hobbyist <$char 'H' <|> Locomotive <$ char 'L'
<|> flip Boiler <$char 'B' <*> (HighPressure <$ char 'H' <|> LowPressure <$char 'L') <*> ((,,) <$ char '-'
<*> (read <$> munch1 isDigit) <* char 'x' <*> (read <$> munch1 isDigit) <* char 'x'
<*> (read <$> munch1 isDigit))  By changing the internal definition of Boiler you can clean up that flip. By also changing the user interface you can use the Read instance deriver: data Boiler = H | L | BL Int Int Int | BH Int Int Int deriving (Read, Show)  Your example would then look like BH 3 3 4. By the way, whereever you're using the Eq instance of Tank, consider pattern matching instead. • Tanks for the answer. I'll look into changing the definitions.But I'm a bit puzzled about the <$ operator ? What does it do? Is it simply a left biased $ or does it do something else? – Ruhrpottpatriot Aug 23 '16 at 16:28 • It is the thing with the characteristic property of having the type signature Functor f => a -> f b -> f a :P. a <$ b = fmap (const a) b. Note the existence of hayoo.fh-wedel.de . – Gurkenglas Aug 23 '16 at 17:22
• Yeah, I searched Hoogle, but found nothing the first time. Second time it was there. Anyway: Additional question: You said I can change my data so I can use the Read instance deriver. Care to elaborate? I'm not sure how to do it. – Ruhrpottpatriot Aug 23 '16 at 21:02
• Using data Boiler = H | L | BL Int Int Int | BH Int Int Int deriving (Read, Show) instead of your definitions of Boiler and Tank will give you access to, for example, readLn :: IO Boiler, which turns an input line like "BH 3 3 4" into a Boiler value. – Gurkenglas Aug 23 '16 at 22:16