#haskell
Вопрос:
Я пытаюсь написать функцию, которая будет сопоставлять IO
исключения MonadError e m
с помощью вспомогательного класса, Injection
. Однако GHC испытывает проблемы с выводом Exception e10
ограничения (?) в следующем примере:
#!/bin/env cabal
{- cabal:
build-depends: base
, mtl
default-extensions: FlexibleInstances
, MultiParamTypeClasses
, ScopedTypeVariables
-}
import Control.Exception
import Control.Monad.Except
main :: IO ()
main =
putStrLn $ "hello cabal"
class Injection a b where
inject :: a -> b
instance Injection a a where
inject = id
try' :: forall m e1 e2 a. (MonadIO m, Exception e1, MonadError e2 m, Injection e1 e2) => IO a -> m a
try' x = do
r <- liftIO (try x)
case r of
Left (e :: e1) -> throwError $ (inject e :: e2)
Right a -> pure a
Я получаю следующую ошибку:
Main.hs:24:9: error:
• Could not deduce (Exception e10)
from the context: (MonadIO m, Exception e1, MonadError e2 m,
Injection e1 e2)
bound by the type signature for:
try' :: forall (m :: * -> *) e1 e2 a.
(MonadIO m, Exception e1, MonadError e2 m, Injection e1 e2) =>
IO a -> m a
at Main.hs:24:9-100
The type variable ‘e10’ is ambiguous
• In the ambiguity check for ‘try'’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
In the type signature:
try' :: forall m e1 e2 a.
(MonadIO m, Exception e1, MonadError e2 m, Injection e1 e2) =>
IO a -> m a
|
24 | try' :: forall m e1 e2 a. (MonadIO m, Exception e1, MonadError e2 m, Injection e1 e2) => IO a -> m a
|
Как я могу написать try'
функцию?
Комментарии:
1. Я подозреваю , что когда вы пишете
Left (e :: e1)
, это связывает новую переменную типа с именемe1
, не связанную с существующей в области видимости.Left e -> throwError $ (inject (e :: e1) :: e2)
Может быть , попробовать?2. …но тебе будет очень больно, даже если ты начнешь
try'
работать. Например, вы не сможете написать какие-либо другие экземпляры дляInjection
. Что заставило вас захотеть этот дизайн? Может быть, мы можем предложить более идиоматичный способ.3. @DanielWagner не
ScopedTypeVariables
должен делатьe1
однозначных выводов в этом случае? Ваше предложение(inject (e :: e1) :: e2)
приводит к той же ошибке. ЭтотInjection
класс просто упрощенControl.Monad.Except.CoHas
. Идея состоит в том, чтобы иметь возможность соединять исключения GHC иMonadError
обработку ошибок с помощью сопоставления классов друг с другом. В принципе, это должно дать вам также возможность построить какую-то иерархию исключений.4. Блин. Я плохо соображал, когда писал свои предыдущие два комментария, оба просто неверны. Мои извинения.
Ответ №1:
Взгляните на этот тип:
try' :: forall m e1 e2 a.
(MonadIO m, Exception e1, MonadError e2 m, Injection e1 e2) =>
IO a -> m a
Когда вы переходите к использованию try'
, абсолютный максимум информации, которую можно предоставить механизму вывода типов по способу его использования, — это указать a
и m
-потому e1
что и e2
не отображаются в тексте типа. В классе есть функциональная зависимость MonadError
, которая говорит, что знание m
также исправляет e2
. Но e1
остается плавающим-может быть много типов , которые являются экземплярами Exception
и могут быть введены e2
, и компилятор не знает, какой из них вы хотите!
В наши дни с TypeApplications
помощью вы можете явно указывать аргументы типа. Если вы согласны с тем, чтобы все пользователи явно указывали свой выбор типов для e1
сайтов вызовов, вы можете применить исправление, предложенное в ошибке: включите AllowAmbiguousTypes
и уходите.