Являются ли неявные параметры трудными для встраивания в GHC?

Мне любопытно, какие возражения против неявных параметров обсуждались в статье Киселева и Шаня " Функциональная жемчужина: неявные конфигурации ".

Встроенный код (β-редуцирование) не имеет смысла при наличии неявных параметров.

В самом деле? Я ожидаю, что GHC должен встроиться в ту же область, что и передаваемый неявный параметр, нет?

Я полагаю, что понимаю их возражение, что:

Поведение термина может измениться, если его подпись добавлена, удалена или изменена.

Пользовательская документация GHC объясняет, что программисты должны заботиться о полиморфной рекурсии и ограничении мономорфизма. Это то, что они подразумевают под проблемой для встраивания?

Я предполагаю, что этот пример полиморфной рекурсии охватывает то, что они подразумевают под "обобщением неявных параметров"? Что-нибудь еще?

Это ReifiesStorable Тип класса из Data.Reflection действительно разумное решение этих трудностей? Кажется, он десериализует всю неявную структуру данных каждый раз, когда к ней обращаются, что может иметь катастрофические последствия для производительности. Например, мы могли бы хотеть, чтобы нашей неявной информацией была таблица Кейли или таблица символов, которая занимает оперативную память и должна быть доступна во время миллионов алгебраических операций.

Может быть, есть какое-то лучшее решение, которое использует неявные параметры, или другой метод, который компилятор может легко оптимизировать за кулисами, в то же время гарантируя большее через систему типов с использованием потоков состояния или что-то еще?

1 ответ

Решение

Да, пример из руководства GHC показывает, как добавление сигнатуры типа может изменить семантику кода с неявными параметрами, и я считаю, что именно это они имеют в виду, ломая встраивание; подача заявления len_acc1 против применения len_acc2 создает один и тот же код, несмотря на разную семантику двух.

Что касается обобщения неявных параметров, это означает, что вы не можете написать функцию, которая может работать с несколькими неявными параметрами; нет механизма абстрагирования над ними, так как неявный параметр, который использует функция, фиксируется ее типом. С помощью отражения вы можете легко написать такую ​​функцию, как doSomethingWith :: (Reifies s a, Num a) => Proxy s -> a, который может работать с любым типом, который увеличивает числовое значение.

Что касается ReifiesStorableвы смотрите на старую версию пакета отражений; последняя версия имеет очень эффективную реализацию, в которой reify стоит столько же, сколько вызов функции.1 Обратите внимание, что даже при старой реализации вы обычно не используете ReifiesStorable класс напрямую, но вместо Reifies, который использует ReifiesStorable чтобы овладеть StablePtrТаким образом, копируется всего несколько байтов, а не весь объект. (Это то же самое, что и оригинальная реализация в документе.) Обе реализации определенно достаточно быстры для практического использования: старая, "медленная" реализация требует около 100 мс для повторного отображения и отображения 100000 значений, а новая реализация - до 10. Миз.

(Полное раскрытие: я работал над новой реализацией.)

1 Быстрая реализация зависит от деталей реализации Haskell. Более старая, более медленная реализация автоматически используется для реализаций Haskell, с которыми быстрая реализация еще не была протестирована; до сих пор было показано, что GHC и Hugs работают с быстрой реализацией. Вы можете запросить медленную реализацию с -fslow, но вряд ли перестанет работать, если GHC существенно не пересмотрит реализацию классов типов. (Даже если это произойдет, вам нужно будет только перекомпилировать пакеты, которые используют отражение, чтобы заставить его работать снова.)

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