openlang

같은 Int, 두 타깃, 두 개의 답

같은 Gleam 모듈을 두 타깃에서 돌리다가 발견했는데, 언어보다는 런타임 쪽 사실이에요.

import gleam/int
import gleam/io

pub fn main() {
  let id = 9_007_199_254_740_993
  io.println(int.to_string(id))
}

gleam run은 9007199254740993을 출력해요. gleam run --target javascript는 9007199254740992를 출력해요. Erlang 타깃에서 Int는 BEAM 정수라서 메모리가 허락하는 만큼 커져요. JavaScript에서는 컴파일러가 Int를 평범한 JS number, 즉 double로 옮기기 때문에 2^53을 넘으면 이웃한 값으로 반올림돼요. 소스의 타입도 코드도 같은데 두 런타임이 조용히 다른 답을 내요. 어느 쪽에서도 오류는 안 나요.

실제 값은 JSON 페이로드에서 왔고, 리터럴은 제일 작은 재현일 뿐이에요. 두 타깃에서 같은 의미여야 하는 정수를 다들 어떻게 다루세요?

영어에서 번역원문 보기

이어지는 대화 3

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

    리터럴은 찾기 쉬운 경우입니다. 각각 안전 범위 한가운데 있는 정수 두 개를 곱해도 곱은 런타임에 2^53을 넘을 수 있습니다. JS 타깃은 제일 가까운 double로 반올림하고 반환합니다. Erlang 타깃은 더 큰 정수를 할당하고 반환합니다. 어느 분기도 아무것도 보고하지 않으므로, 알아챌 수 있는 것은 JavaScript에서 도는 테스트뿐입니다. 패키지가 두 타깃을 모두 지원한다고 한다면 기본 실행 옆에 gleam test --target javascript가 있어야 하고, 테스트에는 작은 값이 아니라 한계를 넘는 값이 들어가야 합니다.

    영어에서 번역원문 보기
  2. 고유 링크

    타깃별로 다르게 하는 부분은, Gleam에서 함수 하나에 한 타깃용 external과 나머지 타깃용 Gleam 본문을 같이 둘 수 있어요. 맞는 external이 없는 곳에서는 본문이 실행돼요.

    @external(javascript, "./int_ffi.mjs", "is_safe")
    pub fn is_safe(_n: Int) -> Bool {
      True
    }
    export function is_safe(n) {
      return Number.isSafeInteger(n);
    }

    Erlang에서는 본문이 True를 돌려주는데, 거기서는 잃는 게 없으니 맞는 답이에요. JavaScript에서는 Number.isSafeInteger가 실제 숫자를 받아요. 이미 반올림된 값도 잡아내요. 한계를 넘은 값이 반올림으로 다시 안전 범위 안에 들어오는 일은 없거든요. 묻는 내용이 런타임에 관한 것이니 답이 그 런타임 쪽 파일에 있는 게 자연스러워요.

    영어에서 번역원문 보기
  3. 고유 링크

    잘 됐어요. 아무도 검사를 건너뛸 수 없게 이제 opaque 타입 뒤에 있어요. pub opaque type Id에 is_safe가 아니라고 하면 Error(Nil)을 돌려주는 from_int를 붙였어요. 이제 JavaScript에서 id용 JSON 디코더는 반올림된 이웃 값을 내주지 않고 거절하고, Erlang은 오류 분기에 한 번도 안 들어가요. 딱 맞는 동작이에요.

    영어에서 번역원문 보기

같은 질문의 다른 갈래

  1. 이 CPS 합계는 왜 Debug에서만 스택이 넘칠까요?

    제가 이해한 게 맞는지 봐 주실 수 있을까요? 연습 삼아 짠 continuation-passing 방식 합계예요. dotnet run -c Release는 1000000을 출력해요. 그냥 dotnet run, 즉 Debug는…

    답글 5fsharp
  2. CTFE는 선언이 요구하는 곳에서만 일어난다

    D의 CTFE를 한동안 잘못 그리고 있었어요. 함수가 컴파일 타임 평가에 적합하면, 인자를 아는 곳 어디서든 컴파일러가 알아서 평가해 줄 거라고 생각했거든요. 실제로는 컴파일 타임 값이 꼭 필요한 곳에서만 해요. 이 코드는…

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

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

    답글 16tcl