Я обеспокоен тем, когда и когда значение класса «глобальное» является общим/memoized, особенно через границы модулей. Я прочитал this и this, но они, похоже, не отражают мою ситуацию, и я вижу некоторое другое поведение от того, что можно было бы ожидать от ответов.повторное использование/memoization глобальных полиморфных значений (класса) в Haskell
Рассмотрим класс, который выставляет значение, которое может быть дорогим, чтобы вычислить:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module A
import Debug.Trace
class Costly a where
costly :: a
instance Num i => Costly i where
-- an expensive (but non-recursive) computation
costly = trace "costly!" $ (repeat 1) !! 10000000
foo :: Int
foo = costly + 1
costlyInt :: Int
costlyInt = costly
И отдельный модуль:
module B
import A
bar :: Int
bar = costly + 2
main = do
print foo
print bar
print costlyInt
print costlyInt
Запуск main
дает две отдельные оценки costly
(как указано след): один для foo
, и один для bar
. Я знаю, что costlyInt
просто возвращает (оценивается) costly
от foo
, потому что если я удалю print foo
от main
, то первый costlyInt
станет дорогостоящим. (. Я могу также привести к costlyInt
чтобы выполнить раздельную оценку независимо от того, что, обобщая тип foo
к Num a => a
)
Я думаю, я знаю, почему такое поведение бывает: экземпляр Costly
фактически является функция, которая принимает Num
словарь и создает словарь Costly
. Поэтому при компиляции bar
и разрешении ссылки на costly
, ghc генерирует новый словарь Costly
, который имеет в нем дорогой кусок. Вопрос 1: Правильно ли я об этом?
Есть несколько способов, чтобы вызвать только один оценку из costly
, в том числе:
- Положите все в одном модуле.
- Удалите ограничение экземпляра
Num i
и просто укажите экземплярCostly Int
.
К сожалению, аналоги этих решений в моей программе нецелесообразны - у меня есть несколько модулей, которые используют значение класса в своей полиморфной форме, и только в исходном файле верхнего уровня используются, наконец, конкретные типы.
Есть также изменения, которые не сократить число оценок, таких как:
- Использование INLINE, INLINABLE или NOINLINE на
costly
определения в экземпляре. (Я не ожидал, что это сработает, но эй, стоит сделать снимок.) - Использование прагмы
SPECIALIZE instance Costly Int
в определении экземпляра.
Последнее удивительно для меня - я ожидал, что это, по существу эквивалентен второй пункт выше, что сделал работы. То есть, я думал, что это сгенерирует специальный словарь Costly Int
, который будет делиться всеми foo
, bar
и costlyInt
. Мой вопрос 2: что мне здесь не хватает?
Мой последний вопрос: существует ли относительно простой и надежный способ получить то, что я хочу, т. Е. Все ссылки на costly
конкретного конкретного типа, разделяемого между модулями? Из того, что я видел до сих пор, я подозреваю, что ответ отрицательный, но я все еще надеюсь.
Как именно этот вопрос отличается от [моего предыдущего] (http://stackoverflow.com/questions/25057803/is-there-an-automatic-way-to-memoise-global-polymorphic-values-in- Haskell)? – leftaroundabout
Из того, что я могу сказать, стоимость в вашем вопросе исходит из рекурсии в сочетании с прохождением словаря, что вызывает экспоненциальную переоценку. Как только последний фиксирован, само вычисление выполняется быстро. Но если два модуля относятся к fibsImplicitDict, они получат разные thunks, которые будут оцениваться отдельно. Я не могу этого себе позволить, потому что мои вычисления по своей сути дороги (не из-за рекурсии). –
PS - факт, что есть экземпляры классов с ограничениями на них, также играет здесь роль. –