Haskell에서 사용자 정의 가드 메커니즘을 정의 할 수 있습니까?
-
20-09-2019 - |
문제
예제를 보면 catches
:
f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex),
Handler (\ (ex :: IOException) -> handleIO ex)]
보입니다 catches
패턴 (두 가지 예외 유형)과 일치하는 사용자 정의 메커니즘을 정의했습니다. 내가 착각 했습니까, 아니면 특정 패턴과 일치하는 람다 함수를 취할 수있는 함수를 정의 할 수 있도록 일반화 될 수 있습니까?
편집 : FYI 아래는 캐치의 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
유형 예외, 즉 - (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