[haskell] Haskell의 엄격함은 무엇입니까?

우리 모두는 Haskell이 기본적으로 게으르다는 것을 알고 있습니다 (또는 알아야합니다). 평가할 때까지 아무것도 평가되지 않습니다. 그렇다면 언제 무언가를 평가해야합니까? Haskell이 엄격해야하는 점이 있습니다. 이 특정 용어가 내가 생각했던 것만 큼 널리 퍼지지는 않았지만 나는 이것을 “엄격 점”이라고 부른다. 나에 따라:

Haskell의 감소 (또는 평가) 는 엄격 성 지점 에서만 발생합니다.

질문은 그래서 : 무엇을 정확하게 , 하스켈의 엄격 포인트입니까? 내 직감에 따르면 main, seq/ 뱅 패턴, 패턴 매칭 및을 IO통해 수행되는 모든 작업 main이 주요 엄격 점이지만 그 이유를 실제로 알지 못합니다.

(그들은 “엄격 포인트”라고하지 않는 경우 또한, 무엇 이다 그들이라고?)

좋은 대답에는 WHNF 등에 대한 토론이 포함될 것이라고 생각합니다. 나는 또한 람다 미적분에 영향을 미칠 것이라고 상상합니다.


편집 :이 질문에 대한 추가 생각.

이 질문에 대해 생각해 보았을 때 엄격 점의 정의에 무언가를 추가하는 것이 더 분명하다고 생각합니다. Strictness points는 다양한 컨텍스트 와 다양한 깊이 (또는 엄격함)를 가질 수 있습니다 . “하스켈에서의 감소는 엄격 성 지점에서만 발생한다”라는 저의 정의로 돌아가서,이 정의에 다음 절을 추가하겠습니다. “엄격 성 지점은 주변 컨텍스트가 평가되거나 축소 될 때만 트리거됩니다.”

그래서 제가 원하는 종류의 답변을 시작하도록하겠습니다. main엄격한 포인트입니다. 특히 문맥의 주요 엄격 점 인 프로그램으로 지정됩니다. 프로그램 ( main의 컨텍스트)이 평가되면 main의 엄격 성 포인트가 활성화됩니다. Main의 깊이는 최대이며 완전히 평가되어야합니다. Main은 일반적으로 IO 작업으로 구성되며, 이는 컨텍스트가 main.

이제 시도해보십시오. seq이러한 용어에 대해 논의 하고 패턴 매칭을 수행하십시오. 함수 적용의 뉘앙스를 설명하십시오 : 얼마나 엄격합니까? 어때요? 어때 deepseq? letcase진술? unsafePerformIO? Debug.Trace? 최상위 수준 정의? 엄격한 데이터 유형? 뱅 패턴? Etc. 이러한 항목 중 몇 개를 시퀀스 또는 패턴 매칭으로 설명 할 수 있습니까?



답변

시작하기 좋은 곳은이 논문을 이해하는 것입니다 : Lazy Evalution을위한 Natural Semantics (Launchbury). 이는 GHC의 Core와 유사한 작은 언어에 대해 표현식이 평가되는시기를 알려줍니다. 나머지 질문은 전체 Haskell을 Core에 매핑하는 방법이며, 대부분의 번역은 Haskell 보고서 자체에 의해 제공됩니다. GHC에서 우리는이 과정을 “desugaring”이라고 부릅니다. 그 이유는 문법적 설탕을 제거하기 때문입니다.

글쎄요, 그게 전체 이야기가 아닙니다. 일찍이). 그래서 정말 방법을 이해하는 당신
, 당신은 코어 GHC에 의해 생산 볼 필요가 프로그램 평가됩니다.

아마도이 대답은 당신에게 약간 추상적 인 것처럼 보이지만 (나는 특별히 뱅 패턴이나 seq를 언급하지 않았습니다), 당신은 정확한 것을 요청 했고 이것은 우리가 할 수있는 최선에 대한 것입니다.


답변

나는 아마도이 질문을 Haskell이 어떤 상황에서 표현을 평가할 것인가? (아마도 “머리가 약한 정상 형태”에 압정을 붙일 것입니다.)

첫 번째 근사치로 다음과 같이 지정할 수 있습니다.

  • IO 작업을 실행하면 “필요한”모든식이 평가됩니다. (따라서 IO 액션이 실행되는지 알아야합니다. 예를 들어 이름이 main인지, 아니면 main에서 호출되었는지 그리고 액션에 필요한 것이 무엇인지 알아야합니다.)
  • 평가중인 표현식 (이건 재귀 적 정의입니다!)은 필요한 모든 표현식을 평가합니다.

직관적 인 목록에서 기본 및 IO 작업은 첫 번째 범주에 속하고 시퀀스 및 패턴 일치는 두 번째 범주에 속합니다. 하지만 첫 번째 범주는 “엄격 점”에 대한 당신의 생각과 더 일치한다고 생각합니다. 사실 그것이 우리가 Haskell에서 평가 를 사용자들에게 관찰 할 수 있는 효과 로 만드는 방법이기 때문입니다 .

Haskell은 큰 언어이기 때문에 모든 세부 사항을 구체적으로 제공하는 것은 큰 작업입니다. Concurrent Haskell은 결국 결과를 사용하지 않더라도 추측 적으로 사물을 평가할 수 있기 때문에 매우 미묘합니다. 이것은 평가를 유발하는 세 번째 유형입니다. 두 번째 범주는 상당히 잘 연구 되어 있습니다. 관련된 기능 의 엄격함 을 살펴보고 싶습니다 . 이 때문에 약간의 사기 비록 첫 번째 범주는 너무, “엄격”의 일종으로 생각 될 수 evaluate xseq x $ return ()실제로 다른 것들! IO 모나드에 일종의 의미를 부여하면 적절하게 처리 할 수 ​​있지만 (명시 적으로 RealWorld#토큰을 전달하는 것은 간단한 경우에 작동 함) 일반적으로 이러한 종류의 계층화 된 엄격 성 분석에 대한 이름이 있는지 모르겠습니다.


답변

C에는 특정 연산에 대해 한 피연산자가 다른 피연산자보다 먼저 평가된다는 것을 보장하는 시퀀스 포인트 개념 이 있습니다. 나는 이것이 가장 가까운 기존 개념이라고 생각하지만 본질적으로 동등한 용어 인 엄격 점 (또는 아마도 강제 점 )은 하스켈의 사고와 더 일치합니다.

실제로 Haskell은 순전히 게으른 언어가 아닙니다.

프로그래머는 또한 seq 결과가 사용되는지 여부에 관계없이 표현식을 강제로 평가 프리미티브를 .

$! 다음과 같이 정의됩니다. seq .

Lazy vs. Non-strict .

에 대한 당신의 생각 그래서 !/ $!seq본질적으로 잘하지만, 패턴 매칭이 미묘한 규칙이 적용이됩니다. ~물론 지연 패턴 일치를 강제 하는 데 항상 사용할 수 있습니다 . 같은 기사에서 흥미로운 점 :

엄격도 분석기는 또한 외부 표현식에 항상 하위 표현식이 필요한 경우를 찾아서이를 즉시 평가로 변환합니다. 의미론 ( “하단”측면에서)이 변경되지 않기 때문에이를 수행 할 수 있습니다.

토끼 구멍 아래로 계속해서 GHC에서 수행하는 최적화 문서를 살펴 보겠습니다.

엄격도 분석은 GHC가 컴파일 타임에 어떤 데이터가 확실히 ‘항상 필요’할지 결정하려고 시도하는 프로세스입니다. 그런 다음 GHC는 계산을 저장하고 나중에 실행하기위한 일반 (더 높은 오버 헤드) 프로세스가 아닌 이러한 데이터를 계산하는 코드를 빌드 할 수 있습니다.

GHC 최적화 : 엄격도 분석 .

즉, 엄격한 코드가 생성 될 수 있습니다 어디서나 썽크를 생성하는 데이터가 항상 필요합니다 (및 / 또는 한 번만 사용할 수 있습니다) 때 불필요하게 고가이기 때문에, 최적화 등.

… 값에 대해 더 이상 평가를 수행 할 수 없습니다. 정상적인 형태 라고합니다 . 값에 대해 최소한 몇 가지 평가를 수행하기 위해 중간 단계 중 하나에 있으면 WHNF ( weak head normal form )가됩니다. ( ‘head normal form’도 있지만 Haskell에서는 사용되지 않습니다.) WHNF에서 무언가를 완전히 평가하면 정상적인 형태로 축소됩니다.

Wikibooks Haskell : 게으름

( 헤드 위치 1 에 베타 redex가없는 경우 용어는 헤드 정규 형식 입니다. redex는 비 redexes 2 의 람다 추상 자만 앞에 오는 경우 헤드 redex 입니다.) 따라서 썽크를 강제하기 시작하면, 당신은 WHNF에서 일하고 있습니다. 강제 할 썽크가 더 이상 남아 있지 않으면 정상적인 상태입니다. 또 다른 흥미로운 점 :

… 어떤 시점에서 사용자에게 z를 인쇄해야하는 경우이를 완전히 평가해야합니다…

이는 실제로 IO수행되는 모든 작업 main 강제 평가를 수행 한다는 것을 의미합니다. 이는 Haskell 프로그램이 실제로 작업을 수행 한다는 점을 고려할 때 분명해야합니다. 에 정의 된 순서를 거쳐야하는 모든 것은 main정상적인 형식이어야하며 따라서 엄격한 평가를 받아야합니다.

그러나 CA McCann은 의견에서 올바르게 설명했습니다. 특별한 것은 특별 main하다고 main정의 된다는 것뿐입니다 . 생성자에 대한 패턴 일치는 IO모나드 에 의해 부과 된 시퀀스를 보장하기에 충분합니다 . 그 점에서만 seq패턴 매칭이 기본입니다.


답변

Haskell은 AFAIK는 순수한 게으른 언어가 아니라 엄격하지 않은 언어입니다. 즉, 가능한 마지막 순간에 반드시 용어를 평가할 필요는 없습니다.

haskell의 “게으름”모델에 대한 좋은 소스는 여기에서 찾을 수 있습니다. http://en.wikibooks.org/wiki/Haskell/Laziness

기본적으로 썽크와 약한 헤더 일반 형식 WHNF의 차이점을 이해하는 것이 중요합니다.

내 이해는 haskell이 명령형 언어에 비해 계산을 거꾸로 끌어 당긴다는 것입니다. 이것이 의미하는 바는 “seq”및 뱅 패턴이없는 경우 궁극적으로 썽크 평가를 강제하는 일종의 부작용이 될 것이며, 이는 차례로 이전 평가 (진정한 게으름)를 유발할 수 있습니다.

이로 인해 끔찍한 공간 누수가 발생하므로 컴파일러는 공간을 절약하기 위해 미리 썽크를 평가하는 방법과시기를 파악합니다. 그런 다음 프로그래머는 엄격 성 주석 (en.wikibooks.org/wiki/Haskell/Strictness, www.haskell.org/haskellwiki/Performance/Strictness)을 제공하여이 프로세스를 지원하여 중첩 된 썽크 형태로 공간 사용량을 줄일 수 있습니다.

저는 haskell의 작동 의미론에 대한 전문가가 아니므로 링크를 리소스로 남겨 두겠습니다.

더 많은 리소스 :

http://www.haskell.org/haskellwiki/Performance/Laziness

http://www.haskell.org/haskellwiki/Haskell/Lazy_Evaluation


답변

게으른 것은 아무것도하지 않는다는 의미가 아닙니다. 프로그램 패턴이 case표현식 과 일치 할 때마다 어떤 것을 평가합니다. 어쨌든 충분합니다. 그렇지 않으면 사용할 RHS를 파악할 수 없습니다. 코드에 케이스 표현식이 보이지 않습니까? 걱정하지 마십시오. 컴파일러는 코드를 사용을 피하기 어려운 Haskell의 제거 된 형태로 변환합니다.

초보자에게 기본적인 경험 법칙 let은 게으르고 case덜 게으르다는 것입니다.


답변

이것은 카르마를 목표로하는 완전한 대답이 아니라 퍼즐의 한 조각 일뿐입니다. 의미론에 관한 것이므로 동일한 의미론 을 제공하는 여러 평가 전략이 있음을 명심하십시오 . 여기에 좋은 예 중 하나가-그리고이 프로젝트는 우리가 일반적으로 하스켈 의미론을 어떻게 생각하는지에 대해 이야기합니다-Eager Haskell 프로젝트로 동일한 의미론 을 유지 하면서 평가 전략을 근본적으로 변경했습니다 : http://csg.csail.mit.edu/ pubs / haskell.html


답변

Glasgow Haskell 컴파일러는 코드를 core 라는 Lambda 미적분과 유사한 언어로 변환합니다 . 이 언어에서는-문으로 패턴을 일치시킬 때마다 무언가가 평가 될 것입니다 case. 따라서 함수가 호출되면 가장 바깥 쪽의 생성자와 그 생성자 (강제 필드가없는 경우) 만 평가됩니다. 다른 것은 덩크에 담겨 있습니다. (Thunk는 let바인딩에 의해 도입됩니다 .)

물론 이것은 실제 언어에서 일어나는 일이 아닙니다. 컴파일러는 매우 정교한 방식으로 Haskell을 Core로 변환하여 가능한 한 많은 것을 지연시키고 항상 필요한 모든 것을 지연시킵니다. 또한 항상 엄격한 unboxed 값과 튜플이 있습니다.

손으로 함수를 평가하려고하면 기본적으로 다음과 같이 생각할 수 있습니다.

  • 반환의 가장 바깥 쪽 생성자를 평가 해보십시오.
  • 결과를 얻기 위해 다른 것이 필요한 경우 (정말 필요한 경우에만)도 평가됩니다. 순서는 중요하지 않습니다.
  • IO의 경우 처음부터 마지막까지 모든 명령문의 결과를 평가해야합니다. IO 모나드는 특정 순서로 평가를 강제하기 위해 몇 가지 트릭을 수행하기 때문에 이것은 조금 더 복잡합니다.