I recently tried to define a machine in the sense of Edward Kmett's Machines library with access to an efficient, mutable array for tracking its state. In order to make use of this kind of array in Haskell, you need to be in the ST monad, so I looked for a way to let a machine run in ST, and preferably without exposing this implementation detail in the machine's signature.

I didn't manage to do it. Perhaps somebody can help me with some wisdom here—I may have gotten something trivial wrong. Or it might just be that my thinking is flawed and it's just not possible. Here's the code I've got so far:

{-# LANGUAGE UnicodeSyntax, MagicHash, Unsafe, UnboxedTuples #-}

-- | Given an initial ST state tag and a 'MachineT' that runs in
-- 'ST', produces a pure 'Machine'.
execST ∷ ∀ s k o. State# s → MachineT (ST s) k o → Machine k o
execST s m = MachineT $ do
    let ST st = runMachineT m
        (# s', stp #) = st s
    case stp of
      Stop →
        return Stop
      Yield o m' →
        return $ Yield o (execST s' m')
      Await f k q →
      return $ Await (execST s' . f) k (execST s' q)

-- | Given a 'MachineT' that runs in 'ST', produces a pure 'Machine'.
execST' ∷ ∀ k o. (∀ s. MachineT (ST s) k o) → Machine k o
execST' m = runST $ ST $ (\s → (# s, execST s m #))

The result of some quick testing was that a machine I ran under execST' would reach its first yield step and then stop.