Haskell의 "아무것도하지 않음"기능인 id가 많은 메모리를 소비하는 이유는 무엇입니까?


112

Haskell에는 입력을 변경하지 않고 반환하는 식별 함수가 있습니다. 정의는 간단합니다.

id :: a -> a
id x = x

따라서 재미를 위해 다음과 같이 출력되어야합니다 8.

f = id id id id id id id id id id id id id id id id id id id id id id id id id id id
main = print $ f 8

몇 초 후 (작업 관리자에 따르면 약 2GB의 메모리) ghc: out of memory. 마찬가지로 통역사는 ghci: out of memory.

id은 매우 간단한 함수 이기 때문에 런타임이나 컴파일 타임에 메모리 부담이 될 것으로 기대하지 않습니다. 사용중인 모든 메모리는 무엇입니까?


11
당신은 그 ids 를 작성하고 싶습니다 . VIM에서의 정의에 커서를두고 다음을 f수행하십시오 :s/id id/id . id ./g..
Tobias Brandt

답변:


135

우리는 id,

id :: a -> a

그리고 우리가를 위해 이것을 전문화하면 id id,의 왼쪽 사본은 다음과 id같은 유형 을 갖습니다.

id :: (a -> a) -> (a -> a)

그런 다음에서 가장 왼쪽 id에 대해 다시 전문화하면 다음과 같은 결과가 나타납니다 id id id.

id :: ((a -> a) -> (a -> a)) -> ((a -> a) -> (a -> a))

따라서 각각 id추가하는 것을 볼 수 있습니다. 맨 왼쪽의 유형 서명 id은 두 배입니다.

유형은 컴파일 중에 삭제되므로 GHC의 메모리 만 차지합니다. 프로그램에서 메모리를 차지하지 않습니다.


Okasaki가 Haskell에 내장 된 RPN 계산기를 작성할 때 비슷한 문제가 발생했던 것을 기억합니다.
dfeuer 2014 년

3
문제는 아마도 GHC가 이런 종류의 일을 좀 더 우아하게 처리 할 방법을 찾아야하는지 여부입니다. 특히 전체를 작성하면 유형이 매우 크지 만 엄청난 양의 중복이 있습니다. 공유를 사용하여 이러한 항목을 압축 할 수 있습니까? 이를 처리하는 효율적인 방법이 있습니까?
dfeuer 2014 년

5
@dfeuer ghci 형식을 요청하십시오. 적절한 공유를해야한다는 응답의 속도를 볼 수 있습니다. 다른 중간 표현 (예 : 핵심)으로 변환하면이 공유가 손실 된 것 같습니다.
Daniel Wagner

4
id이는가 반복 되는 경우 n해당 유형의 공간이에 비례 함을 의미 2^n합니다. Ryan의 코드에서 추론 된 유형 2^27은 유형을 나타내는 데 필요한 다른 구조 외에 유형 변수에 대한 참조 가 필요 합니다. 이는 대부분의 유형이 예상하는 것보다 훨씬 더 클 것입니다.
David

58
유형 추론을 순진하게 수행하는 것은 두 배의 지수입니다. 유형 표현식에서 공유를 교묘하게 사용하면 지수로 낮출 수 있습니다. 그러나 당신이 무엇을하든, 타입 검사기를 폭발시키는 다소 간단한 표현이있을 것입니다. 다행히 실제 프로그래밍에서는 이러한 현상이 발생하지 않습니다.
augustss
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.