У меня есть следующее преобразование монады для работы с ошибками в Haskell.
instance (Monad m, Error e) => Monad (EitherT e m) where
return = EitherT . return . return
m >>= k = EitherT $ do
a <- runEitherT m
case a of
Left l -> return (Left l)
Right r -> runEitherT (k r)
fail = EitherT . return . Left . strMsg
Он работает довольно хорошо, поскольку я могу инстанцировать Error
с пользовательским классом и иметь довольно гибкое средство для обработки ошибок.
fail
немного глуповат, хотя, потому что это тип String -> EitherT e m
, а ограничение String
может быть раздражающим способом создания ошибок. В итоге получается куча всего:
instance Error BazError where
strMsg "foo" = FooError -- oh look we have no error context
strMsg "bar" = BarError -- isn't that nice
Я бы хотел создать новую функцию, например fail
, типа a -> e
, чтобы убрать ограничение (Error e)
. fail
особенно удобен, когда стек монад становится большим, например, когда я получаю
EitherT BazError (StateT [BazWarning] IO) Foo
Есть ли способ создать функцию, которая имеет такое же поведение, как fail
с менее ограничительным типом? Или fail
реализован с помощью глубокой темной магии haskell?