С помощью языковых расширений мы можем создавать ситуации, когда f x
должен оцениваться многократно:
{-# LANGUAGE GADTs, Rank2Types #-}
module MultiEvG where
data BI where
B :: (Bounded b, Integral b) => b -> BI
foo :: [BI] -> [Integer]
foo xs = let f :: (Integral c, Bounded c) => c -> c
f x = maxBound - x
g :: (forall a. (Integral a, Bounded a) => a) -> BI -> Integer
g m (B y) = toInteger (m + y)
x :: (Integral i) => i
x = 3
in map (g (f x)) xs
Суть в том, чтобы f x
полиморфно даже в качестве аргумента g
, и мы должны создать ситуацию, когда тип (ы), в которых он необходим, не может быть предсказан (мой первый удар использовал Either a b
вместо BI
, но при оптимизации это, конечно, приводило только кдве оценки f x
не более).
Полиморфное выражение должно оцениваться как минимум один раз для каждого типа, в котором оно используется.Это одна из причин ограничения мономорфизма.Однако, когда диапазон типов, в которых он может быть необходим, ограничен, можно запомнить значения для каждого типа, и в некоторых случаях GHC делает это (нуждается в оптимизации, и я ожидаю, что число задействованных типов не должно быть слишкомбольшой).Здесь мы сопоставляем его с тем, что по сути является неоднородным списком, поэтому при каждом вызове g (f x)
он может понадобиться для произвольного типа, удовлетворяющего ограничениям, поэтому вычисление не может быть отменено за пределами map
(технически компилятор можетпо-прежнему создает кэш значений для каждого используемого типа, поэтому он будет оцениваться только один раз для каждого типа, но GHC этого не делает, по всей вероятности, это не стоило бы усилий).
- Мономорфные выражения нужно вычислять только один раз, они могут быть разделены.Являются ли они до реализации;по чистоте, это не меняет семантику программы.Если выражение связано с именем, на практике вы можете рассчитывать на его совместное использование, поскольку это легко и очевидно, чего хочет программист.Если это не связано с именем, это вопрос оптимизации.При использовании генератора байт-кода или без оптимизаций выражение часто будет оцениваться повторно, но при оптимизации повторная оценка будет указывать на ошибку компилятора.
- Полиморфные выражения должны оцениваться как минимум один раз для каждого типа, в котором они используются,но с оптимизацией, когда GHC может увидеть, что он может использоваться несколько раз для одного и того же типа, он будет (обычно) все еще использоваться для этого типа во время больших вычислений.
Итог: всегда компилироватьс оптимизациями помогите компилятору связать выражения, которые вы хотите использовать совместно с именем, и, по возможности, предоставьте сигнатуры мономорфного типа.