Can a custom security mechanism be defined in Haskell?

If you look at an example for catches :

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

It seems that catches defined a custom mechanism for matching patterns (two types of exceptions). Am I mistaken, or can this be generalized so that it is possible to define a function that can accept lambda functions that correspond to a specific pattern?

Edit: The FYI below is the source of the GHC for catches. If someone can shed light on how this works, it would be great.

 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 
+6
design-patterns haskell guard
source share
2 answers

These are type variables with the scope of the GHC extension at work. Follow the link to find out more.

Basically, you define a type assertion that must be done with patter before it can match. So yes, that’s akin to the guards, but not completely.

How does this specific example work? Immerse yourself in the sources of the "base" library to find out what:

 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 

We see that IOException and ArithException are different types that implement typeclass Exception . We also see that toException/fromException is a wrapping / deployment mechanism that allows you to convert values ​​of type Exception to / from values ​​of types IOException , ArithException , etc.

So, we could write:

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

Assume that an IOException occurring. When catchesHandler processes the first element of the list of handlers, it raises a tryHandler , which raises fromException . From the definition of tryHandler it follows that the return type fromException must match the handleArith argument. On the other hand, e is of type Exception, namely - (IOException ...). So, types are played out this way (this is not a valid haskell, but I hope you get my thought):

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

From the instance Exception IOException ... immediately follows that the result is Nothing , therefore this handler is skipped. For the same reasons, the next handler will be called because fromException will return (Just (IOException ...)) .

So, you used handleArith and handleIO type signatures to indicate when each would be called, and fromException/toException made sure that it happened.

If you want, you can also restrict the types of handleIO and handleArith inside the definition of f using variables of type region. Perhaps this may give you better readability.

Lastly, scope type variables are not the main players here. They are just used for convenience. The main technique for playing tricks is fromException/toException and friends. The copied type variables allow you to have syntax that more closely resembles security patterns.

+5
source share
 case () of ()| foo expr1 -> handleFooCase | bar expr2 -> handleBarCase | otherwise -> blah 
+1
source share

All Articles