Монада Writer
- узкое место. Лучший способ обобщить ваш код, чтобы он мог работать в этих двух «режимах», - это изменить интерфейс , т.е. , класс LogFunctionCalls
, который будет параметризован монадой:
class Monad m => LogFunctionCalls m where
myTell :: String -> Int -> m ()
Затем мы можем использовать монаду идентичности (или преобразователь монады), чтобы реализовать ее тривиально:
newtype NoLog a = NoLog a
deriving (Functor, Applicative, Monad) via Identity
instance LogFunctionCalls NoLog where
myTell _ _ = pure ()
Обратите внимание, что функция для тестирования теперь имеет другой тип, который больше не относится к Writer
явно:
countNumberCalls :: (LogFunctionCalls m) => Int -> m Int
Давайте вставим его в тест, который имеет все виды методологических проблем, как указано в комментариях, но все же что-то интересное происходит, если мы скомпилируем его с помощью ghc -O
:
main :: IO ()
main = do
let iternumber = 1500000
putStrLn $ "Hello"
t0 <- Time.getCurrentTime
-- Non-monadic version
let n = countNumberCallsNoWriter iternumber
putStrLn $ "Without any writer, the result is " ++ (show n)
t1 <- Time.getCurrentTime
print (Time.diffUTCTime t1 t0)
-- NoLog version
let n = unNoLog $ countNumberCalls iternumber
putStrLn $ "The result is " ++ (show n)
t2 <- Time.getCurrentTime
print (Time.diffUTCTime t2 t1)
Результат:
Hello
Without any writer, the result is 1500000
0.022030957s
The result is 1500000
0.000081533s
Как мы видим, вторая версия (та, которая нам важна) заняла нулевое время. Если мы удалим первую версию из теста, тогда оставшаяся займет 0,022 секунды предыдущего.
Итак, GH C фактически оптимизировал один из двух тестов, потому что увидел, что они одинаковы , который достигает того, что мы изначально хотели: код «журналирования» выполняется так же быстро, как специализированный код, без журналирования, потому что они буквально одинаковы, а числа тестов не имеют значения.
Это также может быть подтверждено глядя на сгенерированное ядро; запустите ghc -O -ddump-simpl -ddump-to-file -dsuppres-all
и разберитесь с файлом Main.dump-simpl
. Или используйте инспекция-тестирование .
Составная сущность: https://gist.github.com/Lysxia/2f98c4a8a61034dcc614de5e95d7d5f8