Какие проверки типов выполняет Raku во время компиляции? Может ли это измениться в будущем?

Aug 19 2020

В настоящее время (по состоянию на август 2020 года) Rakudo не проверяет типы возвращаемых значений функций во время компиляции; то есть, он не предоставляет статических гарантий того, что функции удовлетворяют своим ограничениям возврата. В частности, следующие две функции компилируются как Raku:

sub get-int(--> Int) { 'bug' }
sub get-int($a --> Int} { when $a == 5 { 'Rare bug' }
   default      { 42 }
}

У меня есть два связанных вопроса:

  1. Есть ли способ узнать, какая проверка типов (если таковая имеется) в настоящее время выполняется во время компиляции? (Либо через список, который кто-то написал, где-то в документации, либо в центральном месте в источнике Rakudo) Или это более спонтанно, чем это?

  2. Является ли эта нехватка времени компиляции для проверки типов намеренным дизайнерским решением? Или добавляется дополнительная статическая проверка типов, что было бы неплохо однажды, но пока еще не реализовано?

(Я знаком с отличным ответом Джонатана на вопрос «Штрафы за производительность для типов / ограничений в Raku?» , В котором говорится, что «Raku требует, чтобы ограничения типа, записанные в программу, применялись не позднее, чем во время выполнения» . В этом ответе описаны различные способы избежать запуска -временные затраты на проверки типов, но не описывает, какие проверки типов выполняются во время компиляции (что, безусловно, позволило бы избежать затрат времени выполнения!).

Ответы

12 JonathanWorthington Aug 19 2020 at 12:04

В настоящее время во время компиляции выполняется очень небольшая проверка типов; то, что в основном происходит как побочный эффект статического оптимизатора. Сегодняшние проверки в основном связаны с вызовами подпрограмм, где:

  • Мы можем определить арность вызова и знать, что количество переданных аргументов никогда не совпадет
  • У нас есть буквальные аргументы, и мы видим, что они никогда не совпадут с подписью

Это остаток того времени, когда статический оптимизатор проделал большую работу по встраиванию. В наши дни он встраивает только собственные операторы во время компиляции, а остальное оставляет динамическому оптимизатору виртуальной машины, который гораздо более способен встраивать, а также может удалять (что позволяет спекулятивную оптимизацию, но также означает, что исходные трассировки стека могут быть восстановлены, тогда как статический оптимизатор потерял эту информацию).

Считается желательным делать больше во время компиляции, однако есть некоторые практические вопросы, которые следует учитывать.

  1. Введение дополнительных проверок также может привести к поломке кода, который работал раньше. Рассмотрим модуль с путем кода, который не прошел бы более строгую проверку времени компиляции, но он используется в системах, которые никогда не сталкиваются с этим случаем. Если он начал не компилировать в более новых версиях компилятора, то после обновления компилятора было бы невозможно развернуть эту систему. В общем, это означает, что выполняемые проверки должны изменяться при изменении языковой версии. (Это по-прежнему означает, что люди должны указывать версию языка, на которой они пишут, при написании кода, помните.)
  2. То, что большее количество проверок, выполняемых во время компиляции, «определенно позволит избежать затрат времени выполнения», может быть правдой, но об этом нетривиально рассуждать. Управляемая среда выполнения не может слепо доверять обещаниям, данным в байт-коде, так как это может привести к нарушениям безопасности памяти (что приведет к SIGSEGV или хуже). Это совершенно очевидно верно для такого языка, как Raku, где семантика проверки типов программируется, но это верно для JVM, CLR и так далее. Наибольшие успехи в Raku, связанные с типами, связаны с использованием собственных типов, которые позволяют избежать большого количества выделений и, следовательно, работы по сборке мусора.
  3. Выполнение дополнительных проверок увеличит сложность компилятора, а также время, необходимое для компиляции. Первый из них уже является проблемой; интерфейс компилятора не претерпел каких-либо значительных архитектурных изменений примерно за десять лет. Текущая работа над RakuAST, которая закладывает основу для макросов, также включает в себя почти переписывание внешнего интерфейса компилятора. Усовершенствованная архитектура должна упростить реализацию дальнейших проверок типов во время компиляции, но также рассматривается вопрос о том, как аспекты компиляции могут быть распараллелены, что может позволить компилятору делать больше, не увеличивая время компиляции настенных часов.

После завершения текущего капитального ремонта внешнего интерфейса компилятора введение дополнительных проверок времени компиляции (но включенных только в следующей языковой версии) представляется весьма вероятным - по крайней мере, пока кто-то работает над этим.

Однако в этой области открывается еще более захватывающая возможность: поскольку будет API для программ Raku, и с учетом планов, которые будут объединены для пользовательских проходов компилятора, также скоро появится возможность реализовать средства проверки типов в виде модулей ! Некоторые из них могут привести к проверкам, которые войдут в будущие языковые версии Raku. Другие могут быть весьма специфичными для предметной области и нацелены на более правильное использование данного модуля. Другие могут требовать соблюдения строгих требований, которые не соответствуют духу основного языка, но которые некоторые пользователи языка могут пожелать выбрать.