Введите следующее в GHCi
>:set -XAllowAmbiguousTypes
>import Data.Coerce
>fcm f = coerce . foldMap (coerce . f)
>:t fcm
fcm
:: (Foldable t, Monoid a1, Coercible a1 c, Coercible a2 a1) =>
(a3 -> a2) -> t a3 -> c
Хорошо, это то, что я ожидаю. Скопируйте и вставьте это в файл.
{-# LANGUAGE AllowAmbiguousTypes #-}
import Data.Coerce
fcm :: (Foldable t, Monoid a1, Coercible a1 c, Coercible a2 a1)
=> (a3 -> a2) -> t a3 -> c
fcm f = coerce . foldMap (coerce . f)
Теперь, если вы загрузите это в GHCi, вы получите ошибку -
Weird.hs:7:9: error:
• Couldn't match representation of type ‘a0’ with that of ‘c’
arising from a use of ‘coerce’
‘c’ is a rigid type variable bound by
the type signature for:
fcm :: forall (t :: * -> *) a1 c a2 a3.
(Foldable t, Monoid a1, Coercible a1 c, Coercible a2 a1) =>
(a3 -> a2) -> t a3 -> c
at Weird.hs:(5,1)-(6,30)
• In the first argument of ‘(.)’, namely ‘coerce’
In the expression: coerce . foldMap (coerce . f)
In an equation for ‘fcm’: fcm f = coerce . foldMap (coerce . f)
• Relevant bindings include
fcm :: (a3 -> a2) -> t a3 -> c (bound at Weird.hs:7:1)
А? Откуда взялся a0
? Если вы удалите сигнатуру типа, код снова будет хорошо скомпилирован. Похоже, у нас теперь есть функция, тип которой мы не можем объяснить компилятору, и GHCi не может сказать нам, что это за тип (он может только давать нам ошибки). GHC, похоже, говорит о внутренних переменных типа (в данном случае a0
), которые не предназначены для пользователя.
Это имеет какое-то отношение к черной магии, окружающей Кэррибли? От GHC. Типы :
Note [Kind-changing of (~) and Coercible]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(~) and Coercible are tricky to define. To the user, they must appear as
constraints, but we cannot define them as such in Haskell. But we also cannot
just define them only in GHC.Prim (like (->)), because we need a real module
for them, e.g. to compile the constructor's info table.
Furthermore the type of MkCoercible cannot be written in Haskell
(no syntax for ~#R).
So we define them as regular data types in GHC.Types, and do magic in TysWiredIn,
inside GHC, to change the kind and type.
Правильно ли я понимаю вещи? Или можно написать сигнатуру типа для функции и успешно скомпилировать код? Что бы это ни стоило, вещь, которую я хочу написать, немного проще -
fcm :: (Monoid m, Coercible m b, Coercible b m, Foldable f) => (a -> b) -> f a -> b
fcm f = coerce . foldMap (coerce . f)
sumBy = fcm @Sum