问题 你如何为类型类方法编写重写规则?


请注意以下课程:

class ListIsomorphic l where
    toList    :: l a -> [a]
    fromList  :: [a] -> l a

我也要求 toList . fromList == id。如何编写重写规则来告诉GHC进行替换?


1861
2017-08-20 23:21


起源

这实际上并不“显而易见”。这是你必须明确要求的财产。例如,您可以使用可以转换为列表的树结构,也可以从列表构造,但此标识不一定保留。 - Cubic
这听起来很合理。谢谢。 - MaiaVictor
还有,不可能 l 不同之处 toList 和 fromList 电话?例如 redBlackTreeToBTree = toList . fromList 哪里 redBlackTreeToBTree :: RedBlackTree a -> BTree a。 - Nathan Davis
是的,它可以。考虑到这一点,我想唯一正确的答案是在每个实例中编写一个特定的规则。那可能吗? - MaiaVictor
@NathanDavis我期待 toList . fromList :: ListIsomorphic l => [a] -> [a] (它有一个模糊的“中间实例”,但只有一个),所以我认为不会涉及两个不同的实例。 - Daniel Wagner


答案:


你可以用一个 RULES pragma实现这种简化,但你必须这样做 一点额外的工作 确保通用方法重写规则在您的机会有机会之前不会触发:

{-# RULES
  "protect toList"   toList = toList';
  "protect fromList" fromList = fromList';
  "fromList/toList"  forall x . fromList' (toList' x) = x; #-}

{-# NOINLINE [0] fromList' #-}
fromList' :: (ListIsomorphic l) => [a] -> l a
fromList' = fromList

{-# NOINLINE [0] toList' #-}
toList' :: (ListIsomorphic l) => l a -> [a]
toList' = toList

这是一个愚蠢的例子,表明它有效:

instance ListIsomorphic Maybe where
    toList = error "toList"
    fromList = error "fromList"

test1 :: Maybe a -> Maybe a
test1 x = fromList (toList x)

main = print $ test1 $ Just "Hello"

这打印 Just "Hello" 而不是错误。此外,您可以看到规则触发:

$ ghc -O -ddump-rule-firings --make rewrite-method.hs
[1 of 1] Compiling Main             ( rewrite-method.hs, rewrite-method.o )
Rule fired: protect toList
Rule fired: protect fromList
Rule fired: unpack
Rule fired: unpack
Rule fired: protect toList
Rule fired: protect fromList
Rule fired: fromList/toList
Rule fired: unpack
Rule fired: Class op show
Rule fired: >#
Rule fired: tagToEnum#
Rule fired: Class op showsPrec
Rule fired: Class op showList
Rule fired: ++
Rule fired: unpack-list
Rule fired: foldr/app
Rule fired: unpack-list
Rule fired: unpack-list
Linking rewrite-method.exe ...

10
2017-08-21 05:40