Infix to postfix notation in Haskell (Shunting-yard algorithm) I've written an infix to postfix converter in Haskell using the Shunting-yard algorithm. Example of how it works:

$ ./in2post 
2 + 2
2 2 +
$ ./in2post
1 + 2 * 3
1 2 3 * +
$ ./in2post 
  (5 - 4) * 3+1005/(12-6*(12   -8)  ) 
5 4 - 3 * 1005 12 6 12 8 - * - / +
$ ./in2post 
(2 + 45
2 45 + ERROR


And the source code:

Main.hs

module Main (main) where

import System.IO
import InToPost

main :: IO ()
main = do
    line &lt;- getLine
    let tokens  = tokenise line
        newexpr = shuntYard [] [] tokens
    putStrLn $ untokenise newexpr


InToPost.hs

module InToPost
    ( Token(TNum, TOp)
    , Operator
    , splitTok
    , tokenise
    , untokenise
    , shuntYard
    ) where

import Data.Char (isSpace, isDigit)
import Data.List (groupBy)

data Token = TNum Int
           | TOp Operator
           deriving (Show)
data Operator = Add
              | Sub
              | Mult
              | Div
              | LBrace
              | RBrace
              deriving (Show, Eq)

splitTok :: String -&gt; [String]
splitTok = groupBy (\x y -&gt; isDigit x &amp;&amp; isDigit y) . filter (not . isSpace)

str2tok :: String -&gt; Either String Token
str2tok tkn@(c:_)
    | isDigit c = Right $ TNum $ read tkn
    | otherwise = case tkn of
        "+" -&gt; Right $ TOp Add
        "-" -&gt; Right $ TOp Sub
        "*" -&gt; Right $ TOp Mult
        "/" -&gt; Right $ TOp Div
        "(" -&gt; Right $ TOp LBrace
        ")" -&gt; Right $ TOp RBrace
        _   -&gt; Left  $ "No such operator: \"" ++ tkn ++ "\""

tok2str :: Token -&gt; String
tok2str (TNum t) = show t
tok2str (TOp t) = case t of
    Add  -&gt; "+"
    Sub  -&gt; "-"
    Mult -&gt; "*"
    Div  -&gt; "/"
    _    -&gt; "ERROR"

precedence :: Operator -&gt; Int
precedence Add    = 1
precedence Sub    = 1
precedence Mult   = 2
precedence Div    = 2
precedence LBrace = 3
precedence RBrace = 3

-- shuntYard (Operator stack) (Token Queue) (Token Buffer) = new Token Queue
shuntYard :: [Operator] -&gt; [Token] -&gt; [Either String Token] -&gt; Either String [Token]
shuntYard _ _ (Left s:_) = Left s
shuntYard stack queue [] = Right $ queue ++ map TOp stack
shuntYard stack queue (Right (TNum t):ts) = shuntYard stack (queue ++ [TNum t]) ts
shuntYard stack queue (Right (TOp t):ts) =
    shuntYard ustack uqueue ts
  where
    (ustack, uqueue) = case t of
        LBrace -&gt; (t : stack, queue)
        RBrace -&gt; (stail srest, queue ++ map TOp sstart)
        _      -&gt; (t : ssend, queue ++ map TOp ssops)
    (sstart, srest) = break (==LBrace) stack
    currprec = precedence t
    (ssops, ssend) = span (\op -&gt; precedence op &gt; currprec &amp;&amp; op /= LBrace) stack
    stail :: [a] -&gt; [a]
    stail (x:xs) = xs
    stail [] = []

tokenise :: String -&gt; [Either String Token]
tokenise = map str2tok . splitTok

untokenise :: Either String [Token] -&gt; String
untokenise (Left s) = s
untokenise (Right ts) = unwords . map tok2str $ ts


Please tell me, what are my bad practices here? For example, the use of Either felt really awkward and I'm sure it can be done better. Also, the case expression in str2tok is quite ugly.
main :: IO ()
main = do
  line &lt;- getLine
  putStrLn $ case traverse str2tok $ splitTok line of
    Left s -&gt; s
    Right ts -&gt; unwords $ map tok2str $ shuntYard ts

-- shuntYard (Token Buffer) = new Token Queue
shuntYard :: [Token] -&gt; [Token]
shuntYard ts = concat queue ++ stack where
  (queue, stack) = (`runState` []) $ for ts $ state . \case
    TNum t -&gt; ([TNum t],)
    TOp LBrace -&gt; ([],) . (LBrace :)
    TOp RBrace -&gt; (map TOp *** drop 1) . break (==LBrace)
    TOp t -&gt; (map TOp *** (t:)) . span (\op -&gt; precedence op &gt; precedence t &amp;&amp; op /= LBrace)


Or perhaps:

(queue, stack) = (`runState` []) $ for ts $ \case
  TNum t -&gt; return [TNum t]
  TOp LBrace -&gt; [] &lt;$ modify (LBrace:)
  TOp RBrace -&gt; do
    sstart &lt;- state $ break (==LBrace)
    modify (drop 1)
    return $ map TOp sstart
  TOp t -&gt; do
    ssops &lt;- state $ span $ \op -&gt; precedence op &gt; precedence t &amp;&amp; op /= LBrace
    modify (t:)
    return $ map TOp ssops