Typing the following in 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
Ok, that is what I expect. Copy paste that into a file.
{-# 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)
Now if you load this into GHCi, you get an error -
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)
Huh? Where did a0
come from? If you delete the type signature, the code compiles fine again. Looks like we now have a function whose type we can't explain to the compiler and GHCi cannot tell us what that type is (it can only give us errors). GHC seems to be talking about internal type variables (in this case a0
) which are not user facing.
Does this have something to do with the black magic surrounding Coercible? From GHC.Types:
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.
Am I understanding things correctly? Or is it possible to write a type signature for the function AND have the code compile successfully? For what it's worth, the thing I'm interested in being able to write is slightly simpler -
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