느낌표는 하스켈 선언에서 무엇을 의미합니까?


261

실제 프로젝트를 사용하여 Haskell을 배우려고 할 때 다음 정의를 보았습니다. 각 논점 앞에서 느낌표가 무엇을 의미하는지 이해하지 못하며 제 책에서 언급하지 않은 것 같습니다.

data MidiMessage = MidiMessage !Int !MidiMessage

13
나는 이것이 매우 일반적인 질문이라고 생각합니다. 나는 똑같은 일에 대해 궁금해했던 것을 기억합니다.
cjs 2016 년

답변:


313

엄격 선언입니다. 기본적으로 이는 데이터 구조 값이 작성 될 때 소위 "약한 헤드 일반 양식"으로 평가되어야 함을 의미합니다. 이것이 무엇을 의미하는지 알 수 있도록 예제를 보자.

data Foo = Foo Int Int !Int !(Maybe Int)

f = Foo (2+2) (3+3) (4+4) (Just (5+5))

f위 의 함수 는 평가 될 때 "thunk", 즉 값을 알아 내기 위해 실행할 코드를 반환합니다. 그 시점에서 Foo는 아직 존재하지 않으며 코드 만 있습니다.

그러나 어떤 시점에서 누군가 패턴 일치를 통해 내부를 보려고 할 수 있습니다.

case f of
     Foo 0 _ _ _ -> "first arg is zero"
     _           -> "first arge is something else"

이것은 필요한 것을 수행하기에 충분한 코드를 실행하며 더 이상은 없습니다. 따라서 4 개의 매개 변수가있는 Foo를 만듭니다 (기존이 없으면 내부를 볼 수 없기 때문에). 첫 번째는 테스트하기 4때문에 일치하지 않는 부분 까지 평가해야합니다 .

두 번째는 테스트하지 않기 때문에 평가할 필요가 없습니다. 따라서 6해당 메모리 위치에 저장되지 않고 나중에 평가할 수 있도록 코드 만 저장합니다 (3+3). 누군가가 그것을 볼 때만 6으로 바뀝니다.

그러나 세 번째 매개 변수 !는 앞에 표시되므로 엄격하게 평가됩니다. (4+4)실행 8되고 해당 메모리 위치에 저장됩니다.

네 번째 매개 변수도 엄격하게 평가됩니다. 그러나 여기에 약간 까다로운 부분이 있습니다. 우리는 완전히 평가하는 것이 아니라 정상적인 머리 모양 만 약하게 평가하고 있습니다. 우리가인지 파악하는 것이이 수단 Nothing또는 Just뭔가, 그리고 저장,하지만 우리는 더 이상 이동하지 않습니다. 즉, 우리는 Just 10실제로 저장하지 않고 Just (5+5)내부에 썽크를 평가하지 않은 채로 둡니다. 비록 이것이 이것의 모든 의미가이 질문의 범위를 넘어서고 있다고 생각하지만, 이것은 알아야 할 것이 중요합니다.

BangPatterns언어 확장 을 사용하는 경우 동일한 방식으로 함수 인수에 주석을 달 수 있습니다 .

f x !y = x*y

f (1+1) (2+2)썽크를 반환합니다 (1+1)*4.


16
이것은 매우 도움이됩니다. 그러나 Haskell 용어가 사람들이 "약한 정상 머리 형태", "엄격한"등과 같은 용어로 이해하기가 더 복잡한 지 궁금해 할 수 없습니다. 올바르게 이해하면! 연산자는 간단히 평가하기 위해 익명 블록을 저장하는 대신 평가 된 표현식 값을 저장하는 것을 의미합니다. 그것이 합리적인 해석입니까 아니면 더 많은 것이 있습니까?
David

71
@David 문제는 값을 얼마나 멀리 평가할 것인가입니다. 머리가 약한 일반 형식의 의미 : 가장 바깥 쪽 생성자에 도달 할 때까지 평가합니다. 일반 형식은 평가되지 않은 구성 요소가 남지 ​​않을 때까지 전체 값을 평가 함을 의미합니다. Haskell은 모든 종류의 평가 수준을 허용하므로이를 설명 할 수있는 풍부한 용어가 있습니다. 값별 의미 체계 만 지원하는 언어에서는 이러한 차이점을 찾지 않습니다.
Don Stewart

8
@David : 여기에 더 자세한 설명을 썼습니다 : Haskell : Weak Head Normal Form은 무엇입니까? . 뱅 패턴이나 엄격 주석은 언급하지 않지만을 사용하는 것과 같습니다 seq.
hammar

1
내가 확실히 이해하기 위해 : 게으름은 컴파일 타임에 알 수없는 인수에만 관련이 있습니까? 즉, 소스 코드에 실제로 명령문 (2 + 2) 이 있으면 알려진 숫자를 추가하는 코드 대신 4 로 최적화 됩니까?
Hi-Angel

1
Hi-Angel, 그것은 컴파일러가 얼마나 최적화하고 싶어하는지에 달려 있습니다. 우리는 앞머리를 사용하여 특정 물건을 약한 정상적인 머리 모양으로 만들고 싶다고 말하지만 "이 썽크를 평가하지 않도록하십시오"라고 말할 방법이 없습니다 더 나아가십시오. " 어떤 특정 참조하는 경우보기의 의미 론적 관점에서 썽크 "N = 2 + 2"를 주어, 당신도 말할 수 n을 현재 평가되고 또는 이전에 평가 하였다.
cjs

92

엄격하고 엄격하지 않은 생성자 인수의 차이점을 확인하는 간단한 방법은 정의되지 않은 경우 어떻게 동작하는지입니다. 주어진

data Foo = Foo Int !Int

first (Foo x _) = x
second (Foo _ y) = y

비 엄격한 인수에 의해 평가되지 않기 때문에 second, 전달하는 것은 undefined문제가 발생하지 않습니다

> second (Foo undefined 1)
1

그러나 undefined우리가 값을 사용하지 않더라도 엄격한 인수는 될 수 없습니다 .

> first (Foo 1 undefined)
*** Exception: Prelude.undefined

2
이것은 Curt Sampson의 답변보다 낫습니다. 왜냐하면 !내부 구현 세부 사항을 탐구하지 않고 실제로 사용자가 관찰 할 수있는 효과를 설명하기 때문 입니다.
David Grayson

26

나는 그것이 주석 주석이라고 생각합니다.

Haskell은 순수하고 게으른 기능 언어이지만 때로는 게으름 의 오버 헤드가 너무 많거나 낭비 될 수 있습니다. 따라서이를 처리하기 위해 썽크를 구문 분석하는 대신 함수에 대한 인수를 완전히 평가하도록 컴파일러에 요청할 수 있습니다.

이 페이지에 더 많은 정보가 있습니다 : 성능 / 엄격 성 .


흠, 당신이 언급 한 그 페이지의 예는! 함수를 호출 할 때 그러나 형식 선언에 넣는 것과는 다릅니다. 내가 무엇을 놓치고 있습니까?
David

형식의 인스턴스를 만드는 것도 식입니다. 제공된 인수를 사용하여 형식 생성자를 지정된 형식의 새 인스턴스를 반환하는 함수로 생각할 수 있습니다.
Chris Vest

4
사실, 모든 의도와 목적에 따라 형식 생성자 함수입니다. 부분적으로 적용하거나 다른 기능에 전달할 수 있습니다 (예 : map Just [1,2,3][Just 1, Just 2, Just 3] 등). 나는 완전히 관련이없는 시설뿐만 아니라 그들과 일치하는 패턴을 생각하는 것이 도움이된다는 것을 알았습니다.
cjs 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.