Pode um costume guarda mecanismo de ser definido em Haskell?
-
20-09-2019 - |
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
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