编写涉及rank-n类型的重写规则

时间:2018-03-11 03:32:40

标签: haskell ghc

以下是我面临的非常类似问题的简化版。

考虑以下类型和函数f1

{-# LANGUAGE RankNTypes #-}

newtype D t = D t deriving Functor
newtype T t = T { getT :: t }

f1 :: (forall t'. t' -> D t') -> T t -> D (T t)

请注意,f1实际上可以是id,因为如果我们通过了适用于所有t的函数,我们当然可以将它专门化为:

f1 = id

现在让我们考虑"反向"功能,f2

f2 :: (T t -> D (T t)) -> t -> D t

这" unspecialises"该功能可以按如下方式实现:

f2 f x = getT <$> (f (T x))

我们可以将f2f1组合如下,这基本上是一个身份函数:

g :: (forall t'. t' -> D t') -> t -> D t
g x = f2 (f1 x)

确实,g几乎等同于id函数,实际上我们可以如下定义g

g = id

所以我们建立了f2 . f1 == id

但是当我们写f2 . f1时,我怀疑GHC可能无法将其编译为id,因为f2至少做了一些非常重要的工作。

我想为f2 . f1写一条重写规则,这是我的尝试:

{-# RULES
"f2f1" forall x. f2 (f1 x) = g x
#-}

g可以定义为id,我认为这可能很好。

但不幸的是,这无法编译。我怀疑这是由于f1中排名较高的类型。

我意识到如果我改变了f1的类型签名,如下所示:

f1 :: (t -> D t) -> T t -> D (T t)
f1 f x = T <$> f (getT x) 

我可以编写如下的重写规则:

{-# RULES
"f2f1" forall x. f2 (f1 x) = x
#-}

但现在每当我使用f1时,它都不仅仅是id,而是相当复杂。

有没有办法编写重写规则,例如f2 . f1 == id,而不给f1id样式实现?

更多信息:

请注意,在我的实际问题中,DT都不是新类型。

DFunctor fT实际上是Coyoneda,来自this previous question regarding newtype deriving

2 个答案:

答案 0 :(得分:4)

RULES中的多态自由变量必须具有类型签名。只需使用

{-# RULES
"f2/f1" forall (x :: forall t. t -> D t). f2 (f1 x) = x
  #-}

答案 1 :(得分:2)

HTNW得到的答案比我写得快。但是,让我解决您的潜在问题。如果我定义

g :: (forall t'. t' -> D t') -> t -> D t
g x = f2 (f1 x)

并使用-O2 -ddump-simpl -dsuppress-coercions -dsuppress-idinfo进行编译,我得到了

Clinton.g1
  :: forall t_aGp.
     (forall t'_aqH. t'_aqH -> D t'_aqH) -> t_aGp -> D (T t_aGp)
Clinton.g1 =
  \ (@ t_aGp)
    (x_aqY :: forall t'_aqH. t'_aqH -> D t'_aqH)
    (x1_Xrz :: t_aGp) ->
    x_aqY @ (T t_aGp) (x1_Xrz `cast` ...)

-- RHS size: {terms: 1, types: 0, coercions: 16}
g :: forall t_aqG.
     (forall t'_aqH. t'_aqH -> D t'_aqH) -> t_aqG -> D t_aqG
g = Clinton.g1 `cast` ...

忽略在代码生成中消失的类型参数和强制转换,这基本上就是

g f y = f y

非常好。根据您的重写规则,我们得到

g :: forall t_aqG.
     (forall t'_aqH. t'_aqH -> D t'_aqH) -> t_aqG -> D t_aqG
g =
  \ (@ t_aGr) (x_aqY :: forall t'_aqH. t'_aqH -> D t'_aqH) ->
    x_aqY @ t_aGr

基本上是

g f = f

更好?好吧,有点。他们实际上有点不同。没有重写规则,

g undefined `seq` () = ()

使用规则,

g undefined `seq` () = undefined

就我个人而言,我不想使用改变语法的重写规则,尤其是那些可以降低定义性的重写规则,所以我永远不会写那个。< / p>