openlang

D의 pure 함수는 인자를 통해 값을 바꿀 수 있다

D의 pure는 단어가 주는 느낌보다 약해요. 평가기 실험에서 호출을 캐시하려다가 그제야 알아챘어요.

int[] log;

pure void bump(int[] a)
{
    a[0]++;          // fine, writes through the argument
    // log ~= a[0];  // rejected, touches a mutable global
}

D에서 pure는 변경 가능한 전역이나 정적 상태에 접근하지 않는다는 뜻이에요. 매개변수로 닿을 수 있는 메모리를 바꾸는 건 막지 않아요. 스펙은 이걸 약한 순수성이라고 불러요. 호출을 캐시할 수 있는 건 매개변수가 변경 가능한 간접 참조를 가질 수 없을 때, 즉 immutable 데이터나 그냥 값일 때뿐이고, 그게 강한 순수성이에요. 키워드는 같고, 어느 쪽이 될지는 매개변수 타입이 정해요.

그러니 평가기가 pure 호스트 함수의 결과를 캐시한다면, 속성만 보고 키를 잡을 경우 bump도 캐시되고 두 번째 호출은 쓰기를 건너뛰게 돼요. 대신 강하게 순수한 시그니처로 키를 잡을까요? 아니면 조건 목록이 길어져서 호스트 쪽에서 명시적으로 켜는 속성을 두는 게 더 간단할까요?

이어지는 대화 4

서로의 근거를 따라
  1. 고유 링크

    매개변수를 const(int)[]로 바꾸고 캐시가 조금이라도 안전해지는지 보세요. bump는 컴파일이 안 되니 좋아요. 그런데 const(int)[]를 받는 함수도 여전히 캐시할 수 있는 건 아니에요. const는 이 함수가 쓰지 않는다는 뜻일 뿐이거든요. 호출하는 쪽이 같은 배열에 대한 변경 가능한 참조를 들고 있다가 두 호출 사이에 원소를 바꾸면, 캐시된 결과는 낡은 값이 돼요. 아무도 쓰지 않는다고 약속하는 건 immutable뿐이에요. 그러니 평가기 규칙은 함수 안에 쓰기가 없느냐가 아니라 매개변수의 한정자에 관한 거고, 호스트가 함수를 등록하는 곳마다 그게 적혀 있으면 좋겠어요.

  2. 고유 링크

    컴파일러 자체가 강한 순수성으로 뭔가를 하나요, 아니면 허락일 뿐인가요? DMD가 이미 똑같은 강하게 순수한 호출 두 번을 하나로 취급한다면, 그 분류를 믿고 기대도 된다는 힌트예요. 스펙이 허용만 하는 거라면 평가기의 캐시가 그 분류에 실제로 기대는 첫 번째가 되는 거니까, 누가 매개변수 타입을 느슨하게 바꾸면 실패하는 테스트가 있었으면 해요.

  3. 고유 링크

    C는 반대 방식을 택했고, 선택지를 정리하는 데 도움이 될 수 있습니다. C23은 함수 타입에 붙이는 [[reproducible]]과 [[unsequenced]] 속성을 추가했는데, 비슷한 약속의 두 가지 강도에 해당합니다. 컴파일러는 이를 검사하지 않습니다. 그렇게 표시된 함수가 약속대로 동작하지 않으면 동작은 정의되지 않습니다. D는 pure를 검사하고 캐시 여부는 시그니처를 읽는 쪽에 맡깁니다. C는 최적화기가 약속을 근거로 움직이게 하고 아무것도 검사하지 않습니다. 평가기 캐시라면 D 방식을 따르는 편이 안전합니다. 단, 키는 검사된 시그니처여야 하고, 호스트가 마음대로 켤 수 있는 별도 플래그여서는 안 됩니다.

  4. 고유 링크

    솔직히 DMD가 그걸로 뭘 하는지는 몰라요. 그리고 어차피 최적화기가 우연히 하는 일 위에 뭔가를 짓고 싶지도 않아요. 그러니 맞아요, 그 분류에 진짜로 기대는 건 캐시가 처음이 될 거예요. 호스트 함수를 등록하고, 매개변수 하나를 immutable에서 const로 바꾸고, 등록이 거부되길 기대하는 테스트는 쓰기도 쉬워요. nullbyte님 얘기의 재밌는 부작용도 있어요. 키가 검사된 시그니처라면 제가 말한 명시적 속성은 할 일이 없어지는데, 아쉽지 않아요.

같은 질문의 다른 갈래

  1. expr를 감싸는 중괄호가 바이트코드 컴파일러가 보는 것을 정한다

    중괄호 없는 expr는 할 수만 있다면 Tcl에서 금지하고 싶은 습관이에요! 스타일 지적처럼 보이지만 전혀 아니에요. 중괄호 버전은 0을 찍어요. 바이트코드 컴파일러가 $qty == 3을 식으로 보고, 변수를 한 번 읽고,…

    답글 13tcl
  2. 매크로로는 줄바꿈이 폼을 끝내게 할 수 없다

    매크로로는 줄바꿈이 폼을 끝내게 만들 수 없어요. 자꾸 그렇게 하고 싶어지지만요. 표기는 아주 작아요. 한 줄에 쓴 paint red blue를 (paint red blue)로 읽고, 줄바꿈에서 폼을 닫고, #부터는 주석으로…

  3. 시계를 읽는 규칙은 어떻게 재현하나요?

    가격 결정을 다시 재생해 보려다 못 했어요. 리스펙스로 쓴 규칙 eligible(item)이 호스트 시계와 그때그때 현재인 카탈로그 리비전을 읽고 있어서, 다시 돌리면 다른 세상을 봤거든요. 이제는 요청에 정해 둔 now와…