Advent of code #2 Haskell solution I've tried to solve AoC day 2 challenge in Haskell (here - don't worry, it's not a competition so sharing a solution here is OK).

The goal is to implement a very simple VM with opcodes 1 (add), 2 (mult) and 99 (exit).

I feel like my solution is incredibly verbose. That maybe be because I rely heavily on the state monad (my background is imperative programming, so there's that). Is there anything I could improve without rewriting the whole solution?

Here's my code, thanks for all suggestions: 

import Data.Sequence
import Control.Monad.State
import Data.List.Split

data Machine = Machine {
    mState :: Seq Int,
    mPos :: Int,
    isDone :: Bool
}

opReadHead :: State Machine Int
opReadHead = do
    machine &lt;- get
    return $ index (mState machine) (mPos machine)

opReadAt :: Int -&gt; State Machine Int
opReadAt target = do
    machine &lt;- get
    return $ index (mState machine) target

opForward :: State Machine ()
opForward = do
    machine &lt;- get
    put $ machine { mPos = mPos machine + 1 }

opWrite :: Int -&gt; Int -&gt; State Machine ()
opWrite target what = do
    machine &lt;- get
    put $ machine { mState = update target what (mState machine) }

opReadNext :: State Machine Int
opReadNext = do
    a &lt;- opReadHead
    opForward
    return a

opAdd :: State Machine ()
opAdd = do
    aPtr &lt;- opReadNext
    a &lt;- opReadAt aPtr
    bPtr &lt;- opReadNext
    b &lt;- opReadAt bPtr
    target &lt;- opReadNext
    opWrite target (a + b)

opMult :: State Machine ()
opMult = do
    aPtr &lt;- opReadNext
    a &lt;- opReadAt aPtr
    bPtr &lt;- opReadNext
    b &lt;- opReadAt bPtr
    target &lt;- opReadNext
    opWrite target (a * b)

opExit :: State Machine ()
opExit = do
    current &lt;- get
    put $ current { isDone = True }

isMachineDone :: State Machine Bool
isMachineDone = do
    get &gt;&gt;= (return . isDone)

opcode :: Int -&gt; State Machine ()
opcode 1 = opAdd
opcode 2 = opMult
opcode 99 = opExit

opExecuteNext :: State Machine ()
opExecuteNext = do
    opValue &lt;- opReadNext
    opcode opValue

runCode :: State Machine ()
runCode = do
    done &lt;- isMachineDone
    if done
    then return ()
    else opExecuteNext &gt;&gt; runCode 

evalWith :: Machine -&gt; Int -&gt; Int -&gt; Int
evalWith machine noun verb = do
    fst $ runState (do
        opWrite 1 noun
        opWrite 2 verb
        runCode
        opReadAt 0
        ) machine

main :: IO()
main = do
    fileData &lt;- readFile "input"
    let memory = map read $ splitOn "," fileData
    let machine = Machine {
        mState = fromList memory,
        mPos = 0,
        isDone = False
    }
    let outputs = [(evalWith machine x y, (x, y)) | x &lt;- [0..99], y &lt;- [0..99]]
    print $ snd $ head $ Prelude.filter ((== 19690720) . fst) outputs
```
Use Control.Lens for code this stateful. (Control.Lens.TH must be used to define Machine.) May as well leave out type signatures this homogenous. Control.Monad.Loops often helps against explicit monadic recursion.

opReadAt target = uses mState $ (`index` target)
opReadNext = mPos &lt;&lt;+= 1 &gt;&gt;= opReadAt
opWrite target what = mState %= update target what

opBin op = do
    a &lt;- opReadNext &gt;&gt;= opReadAt
    b &lt;- opReadNext &gt;&gt;= opReadAt
    target &lt;- opReadNext
    opWrite target $ op a b 

opcode 1 = opBin (+)
opcode 2 = opBin (*)
opcode 99 = isDone .= True

runCode = (opReadNext &gt;&gt;= opCode) `untilM_` use isDone

evalWith :: Int -&gt; Int -&gt; Machine -&gt; Int
evalWith noun verb = evalState $ do
    opWrite 1 noun
    opWrite 2 verb
    runCode
    opReadAt 0