Простым подходом является изменение Interp
для включения IO
.
newtype Interp a = Interp { runInterp :: Store -> IO (Either String (a, Store)) }
Тогда нам просто нужно обновить Monad
экземпляр, rd
, wr
и run
для новых внутренних элементов Interp
путем разбрызгивания в некоторых return
с и связывания.Например, вот новый экземпляр Monad
:
instance Monad Interp where
return x = Interp $ \r -> return (Right (x, r))
i >>= k =
Interp $ \r -> do
res <- runInterp i r
case res of
Left msg -> return (Left msg)
Right (x, r') -> runInterp (k x) r'
fail msg = Interp $ \_ -> return (Left msg)
Одним из преимуществ абстрагирования Interp
в первую очередь было то, что мы можем вносить подобные изменения без изменения основной части.переводчика (eval
и exec
) вообще.