[haskell] Haskell에서 평가되는 함수에 대해 어떤 규칙이 있습니까?

제목에서 알 수 있듯이 Haskell 함수 반환 단위를 평가하려면 어떤 보증이 있습니까? 그러한 경우에는 어떤 종류의 평가도 실행할 필요가 없다고 생각할 것입니다. ()엄격한 요구가 명시되어 있지 않으면 컴파일러는 그러한 모든 호출을 즉시 값으로 바꿀 수 있습니다. 반품 ()또는 하단.
나는 GHCi에서 이것을 실험했고, 그 반대의 일, 즉 그러한 기능이 평가 된 것처럼 보입니다. 매우 원시적 인 예는

f :: a -> ()
f _ = undefined

f 1의 존재로 평가 하면 오류가 발생 undefined하므로 일부 평가는 확실히 발생합니다. 그러나 평가가 얼마나 깊이 진행되는지는 확실하지 않습니다. 때로는 함수를 호출하는 모든 호출을 평가하는 데 필요한만큼 깊이있는 것처럼 보입니다 (). 예:

g :: [a] -> ()
g [] = ()
g (_:xs) = g xs

이 코드는로 표시되면 무한정 반복됩니다 g (let x = 1:x in x). 하지만

f :: a -> ()
f _ = undefined
h :: a -> ()
h _ = ()

h (f 1)returns 를 표시하는 데 사용할 수 ()있으므로이 경우 모든 단위 값 하위 표현식이 평가되는 것은 아닙니다. 여기서 일반적인 규칙은 무엇입니까?

ETA : 물론 게으름에 대해 알고 있습니다. 컴파일러 작성자 가이 특별한 경우를 평소보다 더 게으르게 만드는 것을 막고 있습니다.

ETA2 : 예제 요약 : GHC는 ()다른 유형과 똑같이 취급 하는 것으로 보입니다 . 즉, 유형에 거주하는 규칙적인 값이 함수에서 반환되어야하는 문제가있는 것처럼 보입니다 . 그러한 값이 하나만 있다는 사실은 최적화 알고리즘에 의해 사용되지 않는 것 같습니다.

ETA3 : Haskell을 말할 때, 나는 Haskell-H-in-GHC가 아니라 보고서에 의해 정의 된 Haskell을 의미합니다. 내가 상상했던 것 ( ‘독자의 100 %에 의한 것’)만큼 널리 공유되지 않은 가정 인 것 같거나, 아마도 더 명확한 질문을 공식화 할 수 있었을 것이다. 그럼에도 불구하고, 나는 원래 그 함수가 호출되는 것에 대해 어떤 보증 이 있는지 묻기 때문에 질문의 제목을 바꾸는 것을 후회합니다 .

ETA4 :이 질문에 대한 답이 나오고있는 것 같습니다. ( ‘닫는 질문’기능을 찾고 있었지만 ‘자신의 질문에 대한 답변’만 찾았으며 답을 찾을 수 없으므로 해당 경로를 따라 가지 않았습니다.) 아무도 보고서에서 어떤 방법 으로든 결정하지 않은 것을 제기하지 않았습니다. 나는 강력하지만 확실하지 않은 ‘언어를 보장하지 않습니다’라는 대답으로 해석하고 싶어합니다. 우리가 아는 것은 현재 GHC 구현이 그러한 기능의 평가를 생략하지 않는다는 것입니다.

OCaml 앱을 Haskell에 이식 할 때 실제 문제가 발생했습니다. 원래 앱에는 여러 유형의 상호 재귀 구조가 있었고 코드는 assert_structureN_is_correct1..6 또는 7에서 N을 호출하는 여러 함수를 선언했습니다 . 각 함수 는 구조가 실제로 정확하면 단위를 반환하고 그렇지 않은 경우 예외를 던졌습니다. . 또한 이러한 기능은 정확성 조건을 분해 할 때 서로 호출되었습니다. Haskell에서는 Either String모나드를 사용하여 처리하는 것이 더 좋았 으므로 필자는 그렇게했지만 이론적 인 문제로 남아 있습니다. 모든 의견과 답변에 감사드립니다.



답변

유형에 ()가능한 값이 하나뿐 이라는 가정에서 비롯된 ()것이므로 유형 값을 리턴하는 모든 함수 호출 ()이 실제로 값을 생성한다고 가정해야합니다 ().

이것은 Haskell의 작동 방식이 아닙니다. 모든 유형에는 Haskell에 값이 하나 더 있습니다. 즉 , 값, 오류 또는 “bottom”이 없습니다 undefined. 따라서 실제로 평가가 이루어지고 있습니다.

main = print (f 1)

핵심 언어의

main = _Case (f 1) _Of x -> print x   -- pseudocode illustration

또는 짝수 (*)

main = _Case (f 1) _Of x -> putStr "()"

코어 _Case는 다음과 같이 강제하고 있습니다 .

%case[식]을 평가하면 테스트중인 표현식 (“스크 루틴 “)을 강제로 평가합니다. 스크 루틴 값은 %of키워드 다음에 나오는 변수에 묶입니다 …”.

값은 머리 정상 형태를 약하게 만듭니다. 이것은 언어 정의의 일부입니다.

하스켈은 하지 선언적 프로그래밍 언어.


(*) print x = putStr (show x)show () = "()"이므로 show호출을 모두 컴파일 할 수 있습니다.

그 값은 실제로로 알려져 있으며 (), 심지어 그 값 show ()도로 알려져 있습니다 "()". 여전히 받아 들여진 Haskell 시맨틱 (f 1)은 사전에 알려진 문자열을 인쇄하기 전에 값이 약한 머리 정상적인 형태로 강요되도록 요구합니다 "()".


편집 : 고려하십시오 concat (repeat []). 그것이 있어야합니까 [], 아니면 무한 루프 여야합니까?

이것에 대한 “선언적 언어”의 대답은 아마도입니다 []. Haskell의 대답은 무한 루프 입니다.

게으름 “가난한 사람의 선언적 프로그래밍”이지만 여전히 실제 는 아닙니다 .

edit2 : 강제 print $ h (f 1) == _Case (h (f 1)) _Of () -> print ()로만 ; 그 정의에 따르면 답 을 만들기 위해 아무것도 강요 할 필요가 없습니다 .hfhh _ = ()

이별 설명 : 게으름은 raison d’ etre를 가질 수 있지만 정의는 아닙니다. 게으름이 바로 그것입니다. 그것은 모든 값이 초기에 썽크 (thunk)가되는 것으로 정의되며, 이는 요구에 따라 WHNF로 강제된다 main. 특정 상황에 따라 특정 사례에서 바닥을 피하는 데 도움이된다면 그렇게합니다. 그렇지 않다면 아닙니다. 그게 다야

좋아하는 언어로 직접 구현하여 느낌을 얻을 수 있습니다. 그러나 모든 중간 값 을 신중하게 명명 함으로써 표현의 평가를 추적 할 수도 있습니다 .


보고서로 가면 , 우리는

f :: a -> ()
f = \_ -> (undefined :: ())

그때

print (f 1)
 = print ((\ _ ->  undefined :: ()) 1)
 = print          (undefined :: ())
 = putStrLn (show (undefined :: ()))

instance Show () where
    show :: () -> String
    show x = case x of () -> "()"

계속된다

 = putStrLn (case (undefined :: ()) of () -> "()")

이제 보고서 의 패턴 일치 에 대한 3.17.3 절의 형식적 의미

case표현 의 의미론은 [그림 3.1–3.3]에 주어진다. 이러한 ID가 […]을 갖도록 모든 구현이 작동해야합니다.

및 경우 (r)도 3.2 미국

(r)     case  of { K x1  xn -> e; _ -> e } =  
        where K is a data constructor of arity n 

() arity 0의 데이터 생성자이므로

(r)     case  of { () -> e; _ -> e } =  

따라서 전체 평가 결과는 다음과 같습니다 .


답변