Если бы вы использовали тип ReaderT ProducerConfig IO a
напрямую, проблем не было бы, потому что пакет exception предоставляет экземпляр
MonadMask IO
Это говорит о том, что вы можете использовать bracket
с IO
, а другой экземпляр
MonadMask m => MonadMask (ReaderT r m)
Это говорит о том, что если базовая монада является экземпляром MonadMask
, то ReaderT
над этой монадой также является экземпляром MonadMask
.
Обратите внимание, что MonadMask
не является преобразователем, который является частью стека монады. Вместо этого есть ограничение, которое говорит: «этот стек монад поддерживает операции маскирования / брекетинга».
Если вы использовали синоним типа как
type KafkaP a = ReaderT ProducerConfig IO a
проблем также не возникнет, потому что синонимы типа не создают новый тип, они просто дают псевдоним выходному. Все еще можно использовать все существующие экземпляры класса типов для типа.
Вы упоминаете в комментариях, что KafkaP
- это новый тип. Новый тип - это дешевый способ создания нового типа из другого. В основном это конструктор, который содержит значение исходного типа.
И в этом проблема. Поскольку это новый тип, он не разделяет автоматически все экземпляры классов типов старого. Фактически, наличие различных экземпляров классов типов в новых типах является одним из основных мотивов использования новых типов!
Что можно сделать? Что ж, если предположить, что конструктор newtype экспортирован (иногда они скрыты для целей инкапсуляции), вы можете развернуть действия KafkaP
s в ReaderT ProducerConfig IO a
перед отправкой их в bracket
, а затем снова преобразовать результат в KafkaP
снова. Некоторые параметры являются KafkaP
-возвратными функциями, поэтому вам, вероятно, придется добавить и некоторую композицию функций. Возможно, что-то вроде (при условии, что KafkaP
- это имя конструктора, а runKafkaP
- имя соответствующего метода доступа):
KafkaP $ bracket (runKafkaP mkProducer) (runKafkaP . clProducer) (runKafkaP . runHandler)
Вся эта обертка и распаковка утомительна; иногда использование coerce
из Data.Coerce
может помочь. Но это работает только при экспорте конструктора newtype.
Вы также можете подумать о том, чтобы определить свой собственный экземпляр MonadMask
для KafkaP
, используя описанную выше технику. Это может быть сделано, но экземпляры, которые не определены ни в модуле, который определяет класс типов, ни в модуле, который определяет тип, называются потерянными экземплярами и несколько недовольны.