Pergunta

Se você olhar para o exemplo de catches:

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

Parece catches foi definido um mecanismo personalizado para corresponder os padrões (os dois tipos de exceção).Estou enganado, ou isso pode ser generalizado para permitir uma para definir uma função que pode assumir funções lambda que correspondem a um determinado padrão?

Editar:FYI abaixo o GHC origem para as capturas.Se alguém pode lançar alguma luz sobre como isso funciona, seria ótimo.

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
Foi útil?

Solução

Este é o Escopo De Variáveis Do Tipo GHC extensão no trabalho.Siga o link para saber mais.

Basicamente, você pode definir uma asserção do tipo que têm que ser cumpridos, pelo fraseado antes de ele pode corresponder.Então, sim, ele é parecido com os guardas, mas não é completamente isso.

Como este exemplo em particular funciona?Mergulho em fontes da "base" da biblioteca para saber 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 e ArithException são diferentes tipos de implementação da typeclass Exception.Também vemos que toException/fromException é um envolvimento/desencapsulamento mecanismo que permite converter os valores do tipo Exception para/a partir de valores de tipos IOException, ArithException, etc.

Então, nós poderíamos ter escrito:

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

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

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

Suponha que IOException acontece.Quando catchesHandler processos do primeiro elemento dos manipuladores lista, ele chama tryHandler, que chama fromException.A partir da definição de tryHandler segue-se que o tipo de retorno do fromException deve ser o mesmo como argumento de handleArith.Por outro lado, e é do tipo de Exceção, a saber: (IOException ...).Assim, os tipos jogar fora esta forma (isto não é válido haskell, mas eu espero que você receba o meu ponto de vista):

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

Do instance Exception IOException ... segue-se imediatamente que o resultado é Nothing, de modo que este manipulador é ignorado.Pelo mesmo raciocínio é o seguinte manipulador seria chamado, porque fromException iria voltar (Just (IOException ...)).

Então, você já usou o tipo de assinaturas de handleArith e handleIO para especificar quando cada um deles deve ser chamado, e fromException/toException a certeza de que o que aconteceu dessa forma.

Se você quiser, você também pode tipos de restrição de handleIO e handleArith dentro da definição de f, usando escopo de variáveis do tipo.Sem dúvida, isso poderia dar a melhor legibilidade.

Finalizando, Escopo de Variáveis de Tipo não são um primária jogadores aqui.Eles são usados apenas para sua conveniência.Principais máquinas para jogar este tipo de truques é fromException/toException e amigos.Escopo de Variáveis do Tipo permitir-lhe ter uma sintaxe que mais de perto se assemelham a guarda de padrões.

Outras dicas

case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top