Haskells 약한 머리 정상적인 형태


9

나는 자극적 인 것들을 우연히 발견했습니다. 나는 haskell이 약한 머리 정상 형태 (WHNF)로 작동한다는 것을 알고 있으며 이것이 무엇인지 알고 있습니다. 다음 코드를 ghci에 입력하십시오 (저는 : sprint 명령을 사용하여 식을 WHNF로 줄입니다).

let intlist = [[1,2],[2,3]]
:sprint intlist

제공 intlist = _이 차종은 완전히 나에게 감지.

let stringlist = ["hi","there"]
:sprint stringlist 

제공 stringlist = [_,_] 이 이미 나를 혼란. 하지만:

let charlist = [['h','i'], ['t','h','e','r','e']]
:sprint charlist

놀랍게도 charlist = ["hi","there"]

내가 Haskell을 이해하는 한, 문자열은 문자 목록과 다르지 않으며 유형 "hi" :: [Char]과 를 확인하여 확인 된 것으로 보입니다 ['h','i'] :: [Char].

위의 세 가지 예가 모두 동일하거나 (목록 목록) 동일하므로 동일한 WHNF, 즉 _로 축소해야하기 때문에 혼란 스럽습니다. 내가 무엇을 놓치고 있습니까?

감사


관련 이있는 것 같습니다
Bergi

@Bergi은 그 질문은 확실히 관련이 있지만, 어느 쪽도 왜 주소로 보인다되지 "bla"['b','l','a']다르게 나올 것이다.
leftaroundabout

@leftaroundabout 때문에 "bla"과부하가 될 수 있지만, ['b','l','a']것으로 알려져있다 String/ [Char]?
Bergi

1
@Bergi 나도 그 생각,하지만 정말 그럴듯 때문이 아니라 ['b', 'l', 'a']할 수 도 과부하 마찬가지로, 그리고 "bla"경우에만 과부하가 -XOverloadedStrings켜져 있습니다.
왼쪽

2
파서와 관련이 있고 GHCi에만 해당되는 것 같습니까? (GHC 컴파일 코드에서 WHNF를 테스트하는 방법을 모르겠습니다.) 따옴표 자체가 트리거 인 것 같습니다.
chepner

답변:


5

참고 :sprint않습니다 하지 WHNF에 식을 줄일 수 있습니다. 그렇다면 다음이 4아니라 오히려 줄 것입니다 _.

Prelude> let four = 2 + 2 :: Int
Prelude> :sprint four
four = _

오히려, :sprint바인딩의 이름을 취하고 바인딩 값의 내부 표현을 가로 지르며 사용하는 동안 이미 "평가 된 부분"(즉, 생성자 인 부분)을 보여줍니다._ 되지 않은 썽크 (즉, 일시 중단 된 게으른 함수)의 자리 표시 자로 통화). 값이 완전히 평가되지 않으면 WHNF가 아닌 평가가 수행되지 않습니다. (그리고 값이 완전히 평가되면 WHNF뿐만 아니라 얻을 수 있습니다.)

실험에서 관찰하는 것은 다형성 대 단수 숫자 유형, 문자열 리터럴에 대한 다른 내부 표현 대 명시적인 문자 목록 등의 조합입니다. 기본적으로 서로 다른 리터럴 표현식이 바이트 코드로 컴파일되는 방식의 기술적 차이를 관찰하고 있습니다. 따라서 이러한 구현 세부 사항을 WHNF와 관련이 있다고 해석하면 절망적으로 혼동 될 것입니다. 일반적으로:sprint WHNF 및 Haskell 평가의 의미에 대해 배우는 방법이 아니라 디버깅 도구로만 .

실제로 :sprint수행중인 작업 을 이해 하려면 GHCi에서 몇 가지 플래그를 설정하여 표현식이 실제로 처리되는 방식을 확인하고 결국 바이트 코드로 컴파일 할 수 있습니다.

> :set -ddump-simpl -dsuppress-all -dsuppress-uniques

이 후, 우리는 당신의 이유를 볼 수 있습니다 intlist 제공_ :

> let intlist = [[1,2],[2,3]]
==================== Simplified expression ====================
returnIO
  (: ((\ @ a $dNum ->
         : (: (fromInteger $dNum 1) (: (fromInteger $dNum 2) []))
           (: (: (fromInteger $dNum 2) (: (fromInteger $dNum 3) [])) []))
      `cast` <Co:10>)
     [])

당신은 무시할 수 있습니다 returnIO외부 :전화를 시작 부분에 집중할((\ @ a $dNum -> ...

다음 $dNumNum제약 조건 사전입니다 . 이는 생성 된 코드가 아직 type의 실제 유형 a을 해석하지 않았 Num a => [[a]]으므로 전체 표현식은 여전히 ​​적절한 ((사전)) 함수를 호출하는 함수 호출로 표시됩니다.Num 유형 . 다시 말해, 그것은 평가되지 않은 썽크이며 우리는 다음을 얻습니다.

> :sprint intlist
_

반면에 유형을 다음과 같이 지정하십시오. Int 하면 코드가 완전히 다릅니다.

> let intlist = [[1::Int,2],[2,3]]
==================== Simplified expression ====================
returnIO
  (: ((: (: (I# 1#) (: (I# 2#) []))
         (: (: (I# 2#) (: (I# 3#) [])) []))
      `cast` <Co:6>)
     [])

그리고 역시 :sprint출력도 :

> :sprint intlist
intlist = [[1,2],[2,3]]

마찬가지로 리터럴 문자열과 명시 적 문자 목록은 완전히 다른 표현을 갖습니다.

> let stringlist = ["hi", "there"]
==================== Simplified expression ====================
returnIO
  (: ((: (unpackCString# "hi"#) (: (unpackCString# "there"#) []))
      `cast` <Co:6>)
     [])

> let charlist = [['h','i'], ['t','h','e','r','e']]
==================== Simplified expression ====================
returnIO
  (: ((: (: (C# 'h'#) (: (C# 'i'#) []))
         (: (: (C# 't'#)
               (: (C# 'h'#) (: (C# 'e'#) (: (C# 'r'#) (: (C# 'e'#) [])))))
            []))
      `cast` <Co:6>)
     [])

:sprint출력 의 차이는 GHCi가 평가 (명시 적 :생성자) 대 평가되지 않은 ( unpackCString#썽크) 표현의 일부를 아티팩트로 나타냅니다 .

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