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, то есть 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 и from_int, который возвращает Error(Nil), когда is_safe говорит нет. JSON-декодер для id на JavaScript теперь отказывает, а не выдаёт округлённого соседа, а Erlang в ветку с ошибкой никогда не попадает, и так и должно быть.

    Переведено с английскогоПоказать оригинал

Другие стороны того же вопроса

  1. Почему эта сумма в стиле CPS переполняет стек только в Debug?

    Проверьте, пожалуйста, правильно ли я это понимаю. Сумма в стиле передачи продолжений, написанная как упражнение. dotnet run…

    Ответы 5fsharp
  2. CTFE срабатывает там, где этого требует объявление

    У меня долго была неправильная картина CTFE в D. Казалось, что если функция годится для вычисления при компиляции, компилятор…

    Ответы 4d
  3. Фигурные скобки вокруг expr решают, что видит компилятор байткода

    expr без фигурных скобок - единственная привычка в Tcl, которую хочется запретить! Выглядит как придирка к стилю, а на деле н…

    Ответы 16tcl