Можно ли определить собственный механизм защиты в Haskell?

StackOverflow https://stackoverflow.com/questions/2270699

  •  20-09-2019
  •  | 
  •  

Вопрос

Если вы посмотрите на пример для catches:

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

Это выглядит как catches определил собственный механизм для сопоставления шаблонов (два типа исключений).Я ошибаюсь, или это можно обобщить, чтобы можно было определить функцию, которая может принимать лямбда-функции, соответствующие определенному шаблону?

Редактировать:К вашему сведению, ниже указан источник уловов GHC.Если кто-то может пролить свет на то, как это работает, было бы здорово.

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
Это было полезно?

Решение

Это Переменные ограниченного типа Расширение GHC в работе.Перейдите по ссылке, чтобы узнать больше.

По сути, вы определяете утверждение о типе, которому шаблон должен соответствовать, прежде чем он сможет соответствовать.Так что да, это сродни охранникам, но не совсем так.

Как работает этот конкретный пример?Погрузиться исходники "базовой" библиотеки чтобы узнать, что:

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

Мы видим, что IOException и ArithException это разные типы, реализующие класс типов Exception.Мы также видим это toException/fromException — это механизм упаковки/развертывания, который позволяет преобразовывать значения типа Exception к/из значений типов IOException, ArithException, и т. д.

Итак, мы могли бы написать:

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

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

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

Предположим, что IOException случается.Когда catchesHandler обрабатывает первый элемент списка обработчиков, он вызывает tryHandler, который вызывает fromException.Из определения tryHandler следует, что возвращаемый тип fromException должен быть таким же, как аргумент handleArith.С другой стороны, e имеет тип Exception, а именно - (IOException...).Итак, типы действуют следующим образом (это неправильный Haskell, но я надеюсь, что вы поняли мою мысль):

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

Из instance Exception IOException ... отсюда сразу следует, что результат Nothing, поэтому этот обработчик пропускается.По тем же соображениям будет вызван следующий обработчик, потому что fromException вернусь (Just (IOException ...)).

Итак, вы использовали сигнатуры типов handleArith и handleIO указать, когда будет вызываться каждый из них, и fromException/toException убедился, что это произошло именно так.

Если вы хотите, вы также можете ограничить типы handleIO и handleArith внутри определения f, используя переменные ограниченного типа.Возможно, это может улучшить читабельность.

И наконец, переменные ограниченного типа здесь не являются основными игроками.Они используются просто для удобства.Основным механизмом для выполнения подобных трюков является fromException/toException и друзья.Переменные ограниченного типа просто позволяют вам иметь синтаксис, который больше напоминает шаблоны защиты.

Другие советы

case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top