2010-02-16 15 views
6

Si nos fijamos en el ejemplo para catches:¿Se puede definir un mecanismo de protección personalizado en Haskell?

f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex), 
        Handler (\ (ex :: IOException) -> handleIO ex)] 

Parece que catches ha definido un mecanismo de medida para adecuarse a los patrones (los dos tipos de excepciones). ¿Estoy equivocado, o se puede generalizar para permitir que uno defina una función que pueda tomar funciones lambda que coincidan en un cierto patrón?

Editar: FYI a continuación es la fuente de GHC para capturas. Si alguien puede arrojar algo de luz sobre cómo funciona esto, sería genial.

catches :: IO a -> [Handler a] -> IO a 
catches io handlers = io `catch` catchesHandler handlers 

catchesHandler :: [Handler a] -> SomeException -> IO a 
catchesHandler handlers e = foldr tryHandler (throw e) handlers 
    where tryHandler (Handler handler) res 
       = case fromException e of 
       Just e' -> handler e' 
       Nothing -> res 

Respuesta

5

Esta es la extensión Scoped Type Variables GHC en el trabajo. Sigue el enlace para obtener más información.

Básicamente, define una afirmación sobre el tipo que debe cumplir el patrón antes de que pueda coincidir. Entonces, sí, es similar a los guardias, pero no del todo.

¿Cómo funciona este ejemplo en particular? Sumergirse en sources of "base" library para descubrir que:

class (Show e) => Exception e where 
    toException :: e -> SomeException 
    fromException :: SomeException -> Maybe e 

data SomeException = forall e . Exception e => SomeException e 

instance Exception IOException where 
    toException = IOException 
    fromException (IOException e) = Just e 
    fromException _ = Nothing 

instance Exception ArithException where 
    toException = ArithException 
    fromException (ArithException e) = Just e 
    fromException _ = Nothing 

vemos que IOException y ArithException son diferentes tipos de aplicación de la clase de tipos Exception. También vemos que es una toException/fromException/dispositivo de enrollado desenvolver que permite convertir valores de tipo Exception a/de valores de tipos IOException, ArithException, etc.

Así, podríamos haber escrito:

f = expr `catches` [Handler handleArith, 
        Handler handleIO] 

handleArith :: ArithException -> IO() 
handleArith ex = .... 

handleIO :: IOException -> IO() 
handleIO ex = .... 

Supongamos que ocurre IOException. Cuando catchesHandler procesa el primer elemento de la lista de controladores, llama al tryHandler, que llama al fromException. De la definición de tryHandler se deduce que el tipo de retorno de fromException debe ser el mismo que el argumento de handleArith. Por otro lado, e es del tipo Excepción, a saber - (IOException ...). Por lo tanto, los tipos juegan de esta manera (esto no es una Haskell válida, pero espero que mi punto):

fromException :: (IOException ...) -> Maybe ArithException 

Desde el instance Exception IOException ... se deduce inmediatamente que el resultado es Nothing, por lo que este controlador se salta . Por el mismo razonamiento se llamaría al siguiente controlador, porque fromException devolvería (Just (IOException ...)).

Por lo tanto, usted ha utilizado las firmas de tipos de handleArith y handleIO para especificar cuando cada uno de ellos se llamaría, y fromException/toException se aseguraron de que haya sucedido de esta manera.

Si lo desea, también puede restringir los tipos de handleIO y handleArith dentro de la definición de f, utilizando variables de tipo de ámbito. Podría decirse que esto podría darte una mejor legibilidad.

Finalización, tipo de ámbito Las variables no son un jugador principal aquí. Solo se usan por conveniencia. La maquinaria principal para jugar este tipo de trucos es fromException/toException y amigos. Las variables de tipo con alcance solo le permiten tener una sintaxis que se asemeja más a los patrones de protección.

+0

¿Cómo se involucran las variables de tipo de ámbito? No veo que este mecanismo funcione en la fuente de las capturas. – me2

+0

Actualizado para explicar fromException/toException trickery – ADEpt

1
case() of 
()| foo expr1 -> handleFooCase 
    | bar expr2 -> handleBarCase 
    | otherwise -> blah 
Cuestiones relacionadas