Ответ 1
Учитывая, что сказал @Cactus, я считаю, что здесь происходит то, что правило Class op fmap
заменяет ваш fmap
его определение для IO
:
instance Functor IO where
fmap f x = x >>= (pure . f)
Если это происходит повсюду, до того как ваше правило будет запущено, тогда не будет fmap
слева (внутреннее представление GHC) вашего кода для запуска вашего собственного правила.
GHC пытается специализировать методы класса, когда они используются в определенных типах, поэтому, если используется монада fmap
, полностью неизвестна, не будет никакого общего fmap
после того, как он будет сделан специализацией.
Итак, оставшийся вопрос: почему ваше правило срабатывает, когда вы предоставляете заголовок модуля?
module Main where
Ответ заключается в том, как это немного отличается от заголовка модуля по умолчанию, который используется, если вы не предоставляете:
module Main (main) where
Обратите внимание, что это явно экспортирует только main
из модуля. Ваша версия, без списка экспорта, вместо этого экспортирует все, что определено в модуле, как main
, так и fun
.
Когда экспортируется только main
, GHC может вывести, что fun
используется только внутри main
, и встроить его полностью в него, не пытаясь сделать автономную версию. Затем он замечает, что fmap
используется только для IO
и специализируется на них. Или, возможно, это делается в обратном порядке, но конечный результат тот же.
Когда fun
также экспортируется, GHC должен предположить, что пользователи вашего модуля могут захотеть называть его в любой монаде. Поэтому GHC затем компилирует автономную версию fun
для общей монады, которая сохраняет fmap
generic, и ваше правило может запускать эту версию.
Однако даже для явного кода module
правило Class op fmap
срабатывает дважды при компиляции, как если бы оно применялось к двум отдельным fmap
s. Поэтому я подозреваю, что даже в этом случае fun
встроен и специализирован в main
, прежде чем ваше правило упростит его использование только одного fmap
, поэтому встроенная версия, используемая внутри main
, все равно не будет иметь ваше правило применяется к нему.