이것은 @ barsoap의 답변에 대한 추가 정보입니다.
하스켈 예외는 순수 코드를 포함하여 어디에서 던질 수 있지만 IO 모나드에서만 잡힐 수 있습니다. 순수한 코드에 의해 던져진 예외를 잡으려면 순수한 코드를 평가하도록 IO 문에 catch
또는 try
을 사용해야합니다. 그 아무것도 잡을 것 때문에
str2Int :: String -> Int -- shortcut so I don't need to add type annotations everywhere
str2Int = read
main = do
print (str2Int "3") -- ok
-- print (str2Int "a") -- raises exception
eVal <- try (print (str2Int "a")) :: IO (Either SomeException())
case eVal of
Left e -> do -- couldn't parse input, try again
Right n -> do -- could parse the number, go ahead
당신은 SomeException
보다 더 구체적인 뭔가를 사용해야합니다. 위의 코드에서 try
은 read
이 문자열을 구문 분석 할 수없는 경우 Left exception
을 반환하지만 값을 인쇄하려고 할 때 IO 오류가 있거나 숫자가 잘못 될 수있는 경우 다른 문자가 있으면 Left exception
을 반환합니다. (메모리 부족 등).
이제 순수 코드의 예외가 악의적 인 이유는 다음과 같습니다. 입출력 코드가 실제로 결과를 평가하도록 강요하지 않으면 어떻게 될까요? 당신이 이것을 실행하면
main2 = do
inputStr <- getLine
let data = [0,1,read inputStr] :: [Int]
eVal <- try (print (head data)) :: IO (Either SomeException())
case eVal of
Right() -> do -- No exception thrown, so the user entered a number ?!
Left e -> do -- got an exception, probably couldn't read user input
, 당신은 당신이 항상 상관없이 사용자가 입력 한 케이스 문의 Right
지점에서 끝 없음을 확인할 수 있습니다. 이것은 try
에 전달 된 IO 작업이 입력 된 문자열 read
을 시도하지 않기 때문입니다. 목록의 첫 번째 값인 data
을 인쇄합니다.이 값은 상수이며 절대로 목록의 꼬리에 닿지 않습니다. 따라서 case 문의 첫 번째 분기에서 코더는 데이터를 평가한다고 생각하지만 그렇지 않다면 read
은 여전히 예외를 throw 할 수 있습니다.
read
은 사용자가 입력 한 내용을 분석하지 않고 데이터를 직렬화 해제하기위한 것입니다. reads
을 사용하거나 실제 파서 연결자 라이브러리로 전환하십시오.나는 uu-parsinglib을 좋아하지만, parsec, polyparse 등 많은 것들도 좋습니다. 어쨌든 긴 시간이 지나면 여분의 힘이 필요할 것입니다.
곧 나오는 GHC 7.6에는'Text.Read.readEither :: Read a => String -> A String a'와'Text.Read.readMaybe :: Read a => String -> Maybe a'가있을 것입니다. – sdcvvc