haskell - Tipos de error expresivo y composable.
error-handling composition (1)
La biblioteca Control.Monad.Exception permite que se usen excepciones fuertemente tipadas en códigos que no sean de E / S. Esto permite a las funciones generar errores y componer fácilmente con funciones que generan diferentes errores. Por ejemplo:
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
import Prelude hiding (catch)
import Control.Monad.Exception
data FooException = FooException deriving (Show, Typeable)
instance Exception FooException
data BarErrors = BarErrors deriving (Show, Typeable)
instance Exception BarErrors
data BazErrors = BazErrors deriving (Show, Typeable)
instance Exception BazErrors
-- sample functions
foo :: (Throws FooException l) => a -> EM l a
foo a = return a
bar :: (Throws BarErrors l) => a -> EM l a
bar _ = throw BarErrors
baz :: (Throws BazErrors l) => a -> EM l a
baz a = return a
-- using all at once:
allAtOnce :: (Throws FooException l, Throws BarErrors l, Throws BazErrors l) =>
a -> EM l String
allAtOnce x = do
_ <- foo x
_ <- bar x
_ <- baz x
return "success!"
-- now running the code, catching the exceptions:
run :: a -> String
run x = runEM $ allAtOnce x `catch` (/(_ :: FooException) -> return "foo failed")
`catch` (/BarErrors -> return "bar failed")
`catch` (/BazErrors -> return "baz failed")
-- run 3 results in "bar failed"
Para obtener más detalles sobre el uso de esta biblioteca, consulte también los documentos Excepciones tipificadas explícitamente para Haskell y Una jerarquía extensible tipificada dinámicamente de excepciones .
Estoy teniendo problemas con la mejor manera de informar errores en un conjunto de funciones que deberían componer bien, en una biblioteca en la que estoy trabajando.
Concretamente, tengo funciones que se parecen a:
foo, bar, baz :: a -> Maybe a
donde foo
puede fallar de una sola manera (una buena opción para Maybe
), pero bar
y baz
pueden fallar de dos maneras diferentes cada una (una buena Either BarErrors
para Either BarErrors
y Either BazErrors
).
Una solución es crear:
data AllTheErrors = TheFooError
| BarOutOfBeer
| BarBurnedDown
| ...
y haga que todas las funciones devuelvan Either AllTheErrors
, que expresa el rango de errores que puede Either AllTheErrors
una secuencia compuesta de estas funciones a expensas de expresar el rango de errores posibles para cada función individual .
¿Hay alguna manera de conseguir ambos? Tal vez con algo más que composición monádica? ¿O con familias tipo (olas de manos) ...?