Монады Хаскеля и fail, который не требует строки

У меня есть следующее преобразование монады для работы с ошибками в 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?

5
задан So8res 22 December 2011 в 09:51
поделиться