Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I have to coerce this data type by fields, rather than all at once?

I have two types (<->) and (<-->) representing isomorphisms between types:

data Iso (m :: k -> k -> *) a b = Iso { to :: m a b, from :: m b a }
type (<->) = Iso (->)
infix 0 <->

data (<-->) a b = Iso' { to' :: a -> b, from' :: b -> a }
infix 0 <-->

The only difference between the two is that (<->) is a specialization of a more general type.

I can coerce (<-->) isomorphisms easily:

coerceIso' :: (Coercible a a', Coercible b b') => (a <--> b) -> (a' <--> b')
coerceIso' = coerce 

But I get an error when I try the same with (<->) isomorphisms:

coerceIso :: (Coercible a a', Coercible b b') => (a <-> b) -> (a' <-> b')
coerceIso = coerce
{-
src/Data/Iso.hs:27:13: error:
    • Couldn't match type ‘a’ with ‘a'’ arising from a use of ‘coerce’
      ‘a’ is a rigid type variable bound by
        the type signature for:
          coerceIso :: forall a a' b b'.
                       (Coercible a a', Coercible b b') =>
                       (a <-> b) -> a' <-> b'
        at src/Data/Iso.hs:25:1-73
      ‘a'’ is a rigid type variable bound by
        the type signature for:
          coerceIso :: forall a a' b b'.
                       (Coercible a a', Coercible b b') =>
                       (a <-> b) -> a' <-> b'
        at src/Data/Iso.hs:25:1-73

-}

My current work-around is to coerce the forwards and backwards functions separately:

coerceIso :: (Coercible a a', Coercible b b') => (a <-> b) -> (a' <-> b')
coerceIso (Iso f f') = Iso (coerce f) (coerce f')

But why is such a workaround is necessary? Why can't (<->) be coerced directly?

like image 271
rampion Avatar asked Jun 27 '19 14:06

rampion


Video Answer


1 Answers

The problem lies in the roles of the arguments m in your general Iso type.

Consider:

data T a b where
  K1 :: Int    -> T () ()
  K2 :: String -> T () (Identity ())

type (<->) = Iso T

You can't really expect to be able to convert T () () into T () (Identity ()) even if () and Identity () are coercible.

You would need something like (pseudo code):

type role m representational representational =>
          (Iso m) representational representational

but this can not be done in current Haskell, I believe.

like image 95
chi Avatar answered Nov 16 '22 00:11

chi