Прежде всего, забудьте, что class
бизнес, похоже, вы просто хотите функцию.
Эта проблема решается с помощью классов Monad*
: MonadIO
, MonadState
и т. Д. Поэтому, если у вас есть монадическое вычисление, которое может выполнять IO, но которое разрешено делать другие вещи, вы должны принять как введите параметр m
любая монада, которая может выполнять действия ввода-вывода:
foo :: (MonadIO m) => m () -> m ()
foo x = do
liftIO $ putStrLn "prefix"
x
liftIO $ putStrLn "suffix"
Теперь не имеет значения, что такое m
, потому что MonadIO
говорит, как вернуть его к нужным операциям.
Классы Monad*
несколько немодулярны перед лицом новых преобразователей - количество экземпляров, которое вам нужно, является квадратичным по числу преобразователей монад. Существуют различные неоптимальные решения этой проблемы. Если такие вещи касаются вас, вы всегда можете повторно определить класс:
foo :: (Monad m) => (forall a. IO a -> m a) -> m () -> m ()
foo lift x = do
lift $ putStrLn "prefix"
x
lift $ putStrLn "suffix"
Будет ли это делать, зависит от вашего уровня абстракции. Вы захотите первое, если вы пишете библиотеку, на которой строится код контента, и, возможно, второе, если вы пишете библиотеку, на которой строите другой код библиотеки. В любом случае, это немного сложно, потому что стеки монад не коммутируют.