Feeding stateful and stateful-effectful computations into each other
What I'd like to improve on this code is two things:

Reduce the lens piping. I could, of course, get rid of _1, _2 and _3 by using a bespoke record type, but then that introduces its own noise -- sim is the only function that uses this particular combination of I, S and O.


Is this really a problem? I don't understand what could be improved about that part.


The write-back to inp' could easily be accidentally omitted, resulting in not-obviously-wrong-looking, well-typed code that
wouldn't work correctly.


You can move the state-passing of I into a separate function for which that's the only job:
withInput :: ALens' s i -&gt; (i -&gt; StateT s i) -&gt; StateT s ()
withInput l go = do
  i &lt;- use (cloneLens l)
  i' &lt;- go i
  cloneLens l := i'

...
sim = do
  withInput _1 $ \inp -&gt;
    inp' &lt;- ...
    pure inp'
  ...

The result type () of withInput makes it difficult to forget to update the state: the function would fail to typecheck if you forget the last clause.