Skip to content

GND accepts ill-roled coercion when manually defining it won't typecheck

I wanted to see what error message might result from adding join to Monad and trying to derive it via GeneralizedNewtypeDeriving, so I used this code to simulate it:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -ddump-deriv #-}
module CoerceJoin where

import qualified Control.Monad (join)
import           Data.Coerce (coerce)

class MyMonad m where
  join :: m (m a) -> m a

instance MyMonad Maybe where
  join = Control.Monad.join

newtype MyMaybe a = MyMaybe (Maybe a) deriving MyMonad

To my surprise, this actually compiles:

==================== Derived instances ====================
Derived instances:
  instance CoerceJoin.MyMonad CoerceJoin.MyMaybe where
    CoerceJoin.join
      = GHC.Prim.coerce
          (CoerceJoin.join ::
             GHC.Base.Maybe (GHC.Base.Maybe a_axs) -> GHC.Base.Maybe a_axs) ::
          forall (a_axs :: *).
          CoerceJoin.MyMaybe (CoerceJoin.MyMaybe a_axs)
          -> CoerceJoin.MyMaybe a_axs


Generic representation:

  Generated datatypes for meta-information:

  Representation types:

That seemed really odd, given my understanding of roles, so I tried to implement this instance manually:

newtype MyMaybe a = MyMaybe (Maybe a)
instance MyMonad MyMaybe where
  join = coerce (join :: Maybe (Maybe a) -> Maybe a) :: MyMaybe (MyMaybe a) -> MyMaybe a

And now GHC will reject it:

CoerceJoin.hs:18:10:
    Couldn't match representation of type `a0' with that of `a1'
      `a1' is a rigid type variable bound by
           an expression type signature: MyMaybe (MyMaybe a1) -> MyMaybe a1
           at CoerceJoin.hs:18:10
    arising from trying to show that the representations of
      `Maybe (Maybe a0) -> Maybe a0' and
      `MyMaybe (MyMaybe a1) -> MyMaybe a1' are the same
    Relevant role signatures:
      type role Maybe representational
      type role MyMaybe representational
    In the expression:
        coerce (join :: Maybe (Maybe a) -> Maybe a) ::
          MyMaybe (MyMaybe a) -> MyMaybe a
    In an equation for `join':
        join
          = coerce (join :: Maybe (Maybe a) -> Maybe a) ::
              MyMaybe (MyMaybe a) -> MyMaybe a
    In the instance declaration for `MyMonad MyMaybe'
Trac metadata
Trac field Value
Version 7.10.2
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler (Type checker)
Test case
Differential revisions
BlockedBy
Related
Blocking
CC goldfire
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information