为什么我必须按字段强制转换这种数据类型,而不是一次全部强制转换?

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

我有两个类型 (<->)(<-->) 表示类型之间的同构:

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 <-->

两者之间的唯一区别是 (<->) 是更通用类型的特化。

我可以 coerce (<-->) 同构很容易:

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

但是当我尝试使用 (<->) 同构时出现错误:

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

-}

我目前的解决方法是分别强制执行前进和后退功能:

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

但为什么需要这样的解决方法?为什么不能直接强制(<->)

问题在于参数 m 在一般 Iso 类型中的作用。

考虑:

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

type (<->) = Iso T

即使 ()Identity () 是可转换的,您也不能真正期望能够将 T () () 转换为 T () (Identity ())

你需要类似的东西(伪代码):

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

但我相信目前 Haskell 无法做到这一点。

不是直接的答案,但我想分享这个相关技巧:只要 mprofunctor(我怀疑通常是),您可以使用 Yoneda-esque转换为具有代表性参数的等效类型。

newtype ProYo m a b = Yo2 (forall x y. (x -> a) -> (b -> y) -> m x y)

ProYo m 同构于 m,除了它的参数角色是代表性的,通过以下同构:

toProYo :: (Profunctor m) => m a b -> ProYo m a b
toProYo m = ProYo (\f g -> dimap f g m)

fromProYo :: ProYo m a b -> m a b
fromProYo (ProYo p) = p id id

如果我们根据此

定义您的Iso
data Iso m a b = Iso { to :: ProYo m a b, from :: ProYo m b a }

coerceIso 无需修改即可通过。