답변:
사용 Maybe
(또는 사촌 Either
기본적으로 동일한 방식으로 작동하지만 대신에 임의의 값을 반환 할 수 있습니다 Nothing
) 예외 약간 다른 용도로 사용됩니다. Java 용어로는 런타임 예외가 아닌 확인 된 예외가있는 것과 같습니다. 예상치 못한 오류가 아니라 처리해야 할 것으로 예상 되는 것을 나타냅니다 .
따라서 항목이 목록에 없을 가능성이 있기 때문에 같은 함수 indexOf
는 Maybe
값을 반환 합니다. 이것은 null
유형 안전 방식을 사용하여 null
케이스 를 처리해야한다는 점을 제외하고 함수에서 돌아 오는 것과 매우 유사 합니다 . Either
오류 사례와 관련된 정보를 반환 할 수 있다는 점을 제외하고는 동일한 방식으로 작동하므로 실제로는 예외와 비슷합니다 Maybe
.
그렇다면 Maybe
/ Either
접근법 의 장점은 무엇 입니까? 하나, 그것은 언어의 일류 시민입니다. Either
예외를 던지는 것과 사용하는 함수를 비교해 봅시다 . 예외적 인 경우, 유일한 유일한 의지는 try...catch
진술입니다. 이 Either
기능을 위해 기존 조합기를 사용하여 흐름 제어를보다 명확하게 만들 수 있습니다. 다음은 몇 가지 예입니다.
먼저, 그렇지 않은 함수를 얻을 때까지 행에서 오류가 발생할 수있는 여러 함수를 시도한다고 가정 해 봅시다. 오류가 없으면 특별한 오류 메시지를 반환하려고합니다. 이것은 실제로 매우 유용한 패턴이지만를 사용하는 것은 끔찍한 고통 try...catch
입니다. 행복하게도, Either
단지 정상적인 값이므로 기존 함수를 사용하여 코드를 훨씬 명확하게 만들 수 있습니다.
firstThing <|> secondThing <|> throwError (SomeError "error message")
또 다른 예는 옵션 기능입니다. 쿼리를 최적화하는 기능을 포함하여 실행할 여러 기능이 있다고 가정 해 봅시다. 이것이 실패하면 다른 모든 것을 실행하기를 원합니다. 다음과 같은 코드를 작성할 수 있습니다.
do a <- getA
b <- getB
optional (optimize query)
execute query a b
이 두 가지 경우 모두를 사용하는 것보다 명확하고 짧으며 try..catch
더 중요하게 더 의미 론적입니다. 같은 기능을 사용 <|>
하거나하는 것은 optional
당신의 의도가 수 많이 사용하는 것보다 명확 try...catch
항상 예외를 처리 할 수 있습니다.
또한 ! 와 같은 줄로 코드를 정리할 필요는 없습니다if a == Nothing then Nothing else ...
. 치료 Maybe
와 Either
모나드의 요점은 이것을 피하는 것입니다. 전파 의미론을 바인드 함수로 인코딩하여 널 / 오류 검사를 무료로 얻을 수 있습니다. 명시 적으로 확인 해야하는 유일한 시간은 Nothing
주어진 이외의 것을 반환하고 싶을 때뿐입니다. Nothing
심지어 코드를 더 좋게 만드는 표준 라이브러리 함수가 많이 있습니다.
마지막으로, 또 다른 장점은 Maybe
/ Either
유형이 더 간단 하다는 것입니다 . 추가 키워드 또는 제어 구조를 사용하여 언어를 확장 할 필요는 없습니다. 모든 것이 단지 라이브러리 일뿐입니다. 그것들은 단지 정상적인 값이기 때문에 타입 시스템을 더 단순하게 만듭니다 .Java에서는 타입 (예 : 반환 타입)과 throws
사용하지 않을 효과 (예 : 명령문) 를 구별해야합니다 Maybe
. 또한 다른 사용자 정의 유형과 동일하게 작동하므로 언어에 특수한 오류 처리 코드가 없어도됩니다.
또 다른 승리 즉 Maybe
/ Either
펑터와 모나드는, 그들은 일반적으로 기존의 모나드 제어 흐름 기능 (있는 공정한 번호가)와, 활용할 수있는 수단, 다른 모나드와 함께 잘 재생합니다.
즉, 몇 가지주의 사항이 있습니다. 우선, 점검되지 않은 예외를 대체 Maybe
하거나 Either
대체 하지 마십시오 . 0으로 나누는 것과 같은 것을 처리하는 다른 방법이 필요할 때마다 모든 단일 부서가 Maybe
값을 반환하는 것이 고통 스럽기 때문에 간단 합니다.
또 다른 문제는 여러 유형의 오류가 반환되는 것입니다 (이 경우에만 적용됨 Either
). 예외를 사용하면 동일한 함수에서 다른 유형의 예외를 처리 할 수 있습니다. 를 사용 Either
하면 한 가지 유형 만 얻을 수 있습니다. 이는 하위 유형을 지정하거나 생성자로서 모든 다른 유형의 오류를 포함하는 ADT를 통해 극복 할 수 있습니다 (이 두 번째 방법은 Haskell에서 일반적으로 사용되는 방법 임).
아직도, 나는 더 간단하고 유연하기 때문에 Maybe
/ Either
접근법을 선호합니다 .
OpenFile()
던질 수 FileNotFound
또는 NoPermission
또는 TooManyDescriptors
등 없음이 정보를 전달하지 않습니다.if None return None
스타일 문 없이 정보를 스택에 쉽게 보낼 수 있습니다 .가장 중요한 것은 예외와 어쩌면 모나드는 다른 목적을 가지고 있다는 것입니다. 예외는 문제를 나타내는 데 사용되지만 어쩌면 그렇지 않습니다.
"간호사, 5 호실에 환자가 있다면 기다릴 수 있습니까?"
( "if"에 유의 하십시오-의사가 아마도 모나드를 기대하고 있음을 의미합니다 )
None
값을 전파 할 수 있음). 요점 5는 단지 옳습니다… 문제는 다음과 같습니다. 어떤 상황이 명백하게 예외적입니까? 그 결과 …… 많지 않습니다 .
bind
테스트를 None
수행해도 구문 오버 헤드가 발생하지 않는 방식으로 작성할 수 있습니다 . 아주 간단한 예인 C #은 Nullable
연산자를 적절히 오버로드합니다 . None
유형을 사용할 때도 필요 하지 않습니다 . 물론 검사는 여전히 완료되지만 (유형 안전하지만) 무대 뒤에서 코드를 어지럽히 지 않습니다. (5)에 대한 귀하의 이의 제기에 대한 귀하의 이의 제기에도 동일하게 적용되지만 항상 적용되는 것은 아님에 동의합니다.
Maybe
하여 모나드 로 취급하는 요점은 전파를 None
암시 적 으로 만드는 것 입니다. 즉 , None
given 을 반환 None
하려면 특별한 코드를 전혀 작성할 필요가 없습니다. 당신이 일치해야 할 유일한 시간은 당신이 뭔가를하고 싶은 경우입니다 None
. 당신은 if None then None
일종의 진술이 필요하지 않습니다 .
null
정확히 (예를 들어 같은 확인 if Nothing then Nothing
)에 대한 무료 때문에 Maybe
모나드이다. 에 대한 bind ( >>=
) 정의로 인코딩됩니다 Maybe
.
Either
과 같이 동작하는 오류 정보 (예 :)를 전달할 수있는 모나드를 쉽게 작성할 수 있습니다 Maybe
. 이 둘 사이의 전환은 실제로 Maybe
특별한 경우 이기 때문에 실제로는 간단 Either
합니다. (Haskell에서는으로 생각할 수 Maybe
있습니다 Either ()
.)
"아마도"는 예외를 대체하지 않습니다. 예외는 예외적 인 경우에 사용하기위한 것입니다 (예 : DB 연결을 열고 DB 서버는 존재하지 않지만). "아마도"는 유효한 값이 있거나 없을 수있는 상황을 모델링하기위한 것입니다. 키에 대한 사전에서 값을 얻는다고 가정하십시오. 키가 있거나 없을 수 있습니다. 이러한 결과에 대해 "예외적 인"것은 없습니다.
나는 Tikhon의 대답을 두 번째로 생각하지만 모든 사람들이 놓친 매우 중요한 실질적인 포인트가 있다고 생각합니다.
Either
메커니즘은 스레드에 전혀 연결되지 않습니다.오늘날 우리가 실제로보고있는 것은 많은 비동기식 프로그래밍 솔루션이 다양한 Either
스타일의 오류 처리 방식을 채택하고 있다는 것입니다. 다음 링크 중 하나에 자세히 설명 된 Javascript promise를 고려하십시오 .
약속 개념을 사용하면 다음과 같은 비동기 코드를 작성할 수 있습니다 (마지막 링크에서 가져옴).
var greetingPromise = sayHello();
greetingPromise
.then(addExclamation)
.then(function (greeting) {
console.log(greeting); // 'hello world!!!!’
}, function(error) {
console.error('uh oh: ', error); // 'uh oh: something bad happened’
});
기본적으로 약속은 다음과 같은 객체입니다.
기본적으로 계산이 여러 스레드에서 발생할 때 언어의 기본 예외 지원이 작동하지 않기 때문에 약속 구현은 오류 처리 메커니즘을 제공해야하며 이는 Haskell의 Maybe
/ Either
유형 과 유사한 모나드로 판명됩니다 .
Haskell 타입 시스템은 사용자에게의 가능성을 인정해야 Nothing
하지만, 프로그래밍 언어는 종종 예외가 발생하지 않아도됩니다. 즉, 컴파일 타임에 사용자가 오류를 확인했음을 알 수 있습니다.
throws NPE
모든 단일 서명과 catch(...) {throw ...}
모든 단일 메소드 본문 에 추가해야한다는 의미에서 확인하기를 원하지 않을 것입니다 . 그러나 나는 어쩌면와 같은 의미로 검사 시장이 있다고 생각합니다 : null 시스템은 선택 사항이며 유형 시스템에서 추적됩니다.
아마도 모나드는 기본적으로 대부분의 주류 언어에서 "널 의미 오류"확인 (널을 확인해야하는 경우 제외)과 동일하며 거의 동일한 장점과 단점이 있습니다.
Maybe
숫자를 추가 할 수 있으며 결과는 다시 한 번 선택적인 값입니다. a + b
None
Maybe
type 에 적용되지만 Maybe
모나드로 사용 하면 구문을 추가 하여 null-ish 논리를 훨씬 더 우아하게 표현할 수 있습니다.
예외 처리는 팩토링 및 테스트에 큰 고통이 될 수 있습니다. 나는 파이썬이 엄격한 "try ... catch"블록없이 예외를 잡을 수있는 멋진 "with"구문을 제공한다는 것을 알고있다. 그러나 Java에서 예를 들어, catch catch 블록은 크고, 상용구이거나, 장황하거나 매우 장황하며 분해하기가 어렵습니다. 또한 Java는 확인 된 예외와 확인되지 않은 예외에 대한 모든 노이즈를 추가합니다.
대신 모나드가 예외를 잡아서 처리 이상이 아닌 모나드 공간의 속성으로 취급하면 던지거나 잡는 것에 관계없이 해당 공간에 바인딩하는 함수를 자유롭게 혼합하고 일치시킬 수 있습니다.
그래도 모나드가 예외가 발생할 수있는 상황 (예 : null 체크를 Maybe로 밀어 넣기)을 방지하면 더 좋습니다. 만약 ... 그런 다음 시도하는 것보다 훨씬, 팩터링하고 테스트하기가 훨씬 쉽습니다.
내가 본 것에서 Go는 각 함수가 반환 (응답, 오류)하도록 지정하여 비슷한 접근 방식을 취하고 있습니다. 이는 핵심 응답 유형이 오류 표시로 장식 된 모나드 공간으로 함수를 "리프팅"하는 것과 같으며, 예외적으로 던지기 및 잡기 예외를 효과적으로 처리합니다.