Haskell에서 오류를보고하는 가장 깨끗한 방법


22

나는 Haskell을 배우기 위해 노력하고 있으며 내가 작성한 함수의 오류를 처리하는 세 가지 방법을 발견했습니다.

  1. 간단히 쓸 수 있습니다 error "Some error message.". 예외가 발생합니다.
  2. Maybe SomeType반환하려는 것을 반환하거나 반환하지 못할 수있는 return 함수를 가질 수 있습니다 .
  3. 내 함수 return을 Either String SomeType사용하여 오류 메시지 또는 처음에 반환하라는 메시지를 반환 할 수 있습니다.

내 질문은 : 오류를 처리하는 방법과 사용해야하는 이유는 무엇입니까? 상황에 따라 다른 방법을 사용해야합니까?

나의 현재 이해는 :

  • 순전히 기능적인 코드에서 예외를 처리하는 것은 "어려워", 하스켈에서는 가능한 한 순전히 기능을 유지하기를 원합니다.
  • Maybe SomeType함수가 실패하거나 성공하면 (즉, 실패 할 수있는 다른 방법 이없는 경우) 리턴하는 것이 올바른 방법 입니다.
  • Either String SomeType함수가 다양한 방법 중 하나로 실패 할 수있는 경우 리턴 은 올바른 조치입니다.

답변:


32

하스켈의 첫 번째 오류 처리 규칙 : 사용하지 마십시오error .

그것은 모든면에서 끔찍합니다. 그것은 순수한 역사의 행위로 존재하며 Prelude가 그것을 사용한다는 사실은 끔찍합니다. 사용하지 마십시오.

당신이 그것을 사용할 수있는 유일한 시간은 무언가가 내부적으로 끔찍한 것이므로 무언가가 현실의 구조에 잘못되어 프로그램의 결과를 무질서하게 만드는 것입니다.

이제 질문은 Maybevs가 Either됩니다. Maybehead값을 반환하거나 반환하지 않을 수도 있지만 실패 할 수있는 이유는 하나 뿐인에 적합합니다. Nothing"파산했고, 이미 이유를 알고 있습니다"와 같은 말입니다. 일부는 그것이 부분적인 기능을 나타낸다고 말합니다.

가장 강력한 오류 처리 형식은 Either+ 오류 ADT입니다.

예를 들어 내 취미 컴파일러 중 하나에서

data CompilerError = ParserError ParserError
                   | TCError TCError
                   ...
                   | ImpossibleError String

data ParserError = ParserError (Int, Int) String
data TCError = CouldntUnify Ty Ty
             | MissingDefinition Name
             | InfiniteType Ty
             ...

type ErrorM m = ExceptT CompilerError m -- from MTL

이제 오류 유형을 정의하고 중첩하여 하나의 영광스러운 최상위 오류가 발생합니다. 이는 컴파일 단계에서 발생하는 오류이거나 ImpossibleError컴파일러 버그를 나타내는입니다.

이러한 각 오류 유형은 예쁜 인쇄 또는 기타 분석을 위해 가능한 한 많은 정보를 유지하려고합니다. 더 중요한 것은 문자열이 없기 때문에 형식 검사기를 통해 잘못된 형식의 프로그램을 실행하면 실제로 통일 오류가 발생하는지 테스트 할 수 있습니다! 일단 무언가가 있으면 String영원히 사라지고 포함 된 모든 정보는 컴파일러 / 테스트에 불투명하므로 Either String그다지 좋지 않습니다.

마지막으로이 유형을 ExceptTMTL의 새로운 모나드 변압기에 넣습니다 . 이것은 본질적 EitherT으로 순수하고 즐거운 방법으로 오류를 던지고 잡을 수있는 멋진 기능 모음과 함께 제공됩니다.

마지막으로, Haskell은 예외를 잡는 것이 예외라는 것을 제외하고 다른 언어와 마찬가지로 예외를 처리 할 수있는 메커니즘을 가지고 있다고 언급 할 가치가 있습니다 IO. 어떤 사람들 IO은 잠재적으로 모든 것이 실패 할 수있는 무거운 응용 프로그램에 이것을 사용하기를 좋아 하지만 가끔은 그것에 대해 생각하고 싶지 않습니다. 이러한 불규칙한 예외를 사용하든 아니면 ExceptT Error IO실제로는 맛의 문제입니다. 개인적으로 나는 ExceptT실패의 기회를 생각 나게 하기 때문에 선택합니다 .


요약하면

  • Maybe -한 가지 확실한 방법으로 실패 할 수 있습니다
  • Either CustomType -실패 할 수 있고 무슨 일이 있었는지 말해 줄게
  • IO+ 예외-때때로 실패합니다. 내가 할 때 던지는 것을 보려면 내 문서를 확인하십시오.
  • error -나도 너 싫어

이 대답은 나를 위해 많은 것을 정리합니다. 나는 내장 함수가 head또는 last사용하는 것처럼 보이는 사실에 근거하여 나 자신을 두 번 추측했다 error. 이것은 그 질문에 대한 답입니다. :)
CmdrMoozy

5
@CmdrMoozy 도움을 주셔서 감사합니다 :) 전주곡에 나쁜 습관이 있다는 것은 정말 불행한 일입니다. 그것은 유산의 길입니다 : /
Daniel Gratzer

3
+1 요약이 훌륭합니다
recursion.ninja

2

나는 몇 가지 방법으로 실패 할 수 있는지에 따라 2와 3을 분리하지 않을 것입니다. 가능한 방법이 하나만 있다고 생각하면 다른 방법으로 얼굴을 보여줍니다. 대신 메트릭은 "내 호출자가 실패 했는지 걱정 합니까? 실제로 실제로 무엇을 할 수 있습니까? "여야합니다 .

그 외에도 Either String SomeType오류 조건을 생성 할 수 있다는 것이 즉시 명확하지 않습니다 . 더 설명적인 이름을 가진 조건으로 간단한 대수 데이터 형식을 만들 것입니다.

사용하는 문제는 직면 한 문제의 특성 및 작업중인 소프트웨어 패키지의 관용구에 따라 다릅니다. 비록 # 1을 피하는 경향이 있지만.


실제로 Either오류에 사용하는 것은 Haskell에서 매우 잘 알려진 패턴입니다.
Rufflewind

1
@rufflewind- Either명확하지 않은 부분은 아니며 String실제 문자열이 아닌 오류로 사용 됩니다.
Telastyn

아, 네 의도를 잘못 읽었 어
Rufflewind
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.