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.