具有幻影参数的类型类实例的强制

时间:2018-10-02 11:09:42

标签: haskell typeclass coercion phantom-types

我有

{-# LANGUAGE RankNTypes, TypeInType #-}

import Data.Coerce
import Data.Kind

newtype IFix f i = IFix { unIFix :: f (IFix f) i }

class IFunctor (f :: (i -> Type) -> i -> Type) where
  imap :: (forall i'. a i' -> b i') -> (forall i'. f a i' -> f b i')

f :: IFunctor f => forall i. IFix f i -> IFix f i
f = undefined

g :: IFunctor f => forall i. IFix f i -> IFix f i
g = IFix . imap f . unIFix

h :: IFunctor f => forall i. IFix f i -> IFix f i
h = coerce . imap f . coerce

i :: IFunctor f => forall i. IFix f i -> IFix f i
i = coerce (imap f)

其中IFix是索引类型构造函数的类型级固定点,i是索引(幻像参数),IFunctor是此类索引类型构造函数的类,实际上函子,f只是一个随机函数,ghi尝试在f包装器下传播IFix。旁注:这是一个简化的示例,实际上,在其他情况下,我常常遇到类似的问题,在这些情况下,手动重新包装变得有些乏味(因为我想避免将unwrap-reraprap函数映射到列表或其他结构上)。

input:18:5: error:
    • Couldn't match representation of type ‘f (IFix f) i’
                               with that of ‘f0 (IFix f1) i0’
        arising from a use of ‘coerce’
   |
18 | h = coerce . imap f . coerce
   |     ^^^^^^

input:21:5: error:
    • Couldn't match representation of type ‘f (IFix f) i’
                               with that of ‘f2 (IFix f3) i1’
        arising from a use of ‘coerce’
   |
21 | i = coerce (imap f)
   |     ^^^^^^^^^^^^^^^

说实话,我并不感到惊讶,但是由于我没有看到强迫症,所以有没有办法修改我的定义,以便可以使用coerce?我尝试过RoleAnnotations,但是

  1. type role IFix nominal phantom无效
  2. 我不知道如何为类型类要求类型角色(我担心这是没有办法的,因为文档中提到类型类假定参数是名义上的)

所以我的问题是,在这种情况下是否有办法使强制工作;如果没有,是否有任何深层原因?或者仅仅是当前角色推断实现的局限性。我的幼稚观点使我相信类型类可以对参数的角色施加约束,而实例必须满足约束。也许有一些有用的强制技巧的好来源吗?

1 个答案:

答案 0 :(得分:1)

实际上,gh不会进行类型检查,因为imap f的类型是模棱两可的。

imap :: IFunctor f => (a ~> b) -> (f a ~> f b)

例如,在h中,imap f被推断为以下类型,带有自由统一变量?f0?f1?i0

h = coerce . (imap f :: ?f0 (IFix ?f1) ?i0 -> ?f0 (IFix ?f1) ?i0) . coerce

通常情况下,上下文允许我们实例化这些变量,但是这里的上下文是:

coerce . _ . coerce

请注意coerce :: Coercible a b => a -> b的类型,就类型推断而言,它完全将输入和输出类型解耦。

我们可以使用扩展名ScopedTypeVariables来注释imap f

h :: forall f i. IFunctor f => IFix f i -> IFix f i
h = coerce . (imap f :: f (IFix f) i -> f (IFix f) i) . coerce

或专门使用coerce来限制其形状:

type E a = a -> a

i :: IFunctor f => IFix f i -> IFix f i
i = (coerce :: E (f (IFix f) i) -> E (IFix f i)) (imap f)

coerce本身太笼统,因此通常必须添加此类注释。拥有共同的专业知识库可能是值得的。