【发布时间】:2018-02-06 13:31:19
【问题描述】:
在 Haskell 中尝试融合中间 trimaps 时出现此问题。
考虑 Peano 自然数的 trie:
data Nat = Zero | Succ Nat
data ExpoNat a = ExpoNat (Maybe a) (ExpoNat a)
| NoExpoNat
我们可以轻松地在ExpoNat 上定义折叠(它本质上是一个列表)并使用foldr/build(又名finally tagless)融合中间出现的ExpoNat:
{-# NOINLINE fold #-}
fold :: (Maybe a -> b -> b) -> b -> ExpoNat a -> b
fold f z (ExpoNat x y) = f x (fold f z y)
fold f z NoExpoNat = z
{-# NOINLINE build #-}
build :: (forall b. (Maybe a -> b -> b) -> b -> b) -> ExpoNat a
build f = f ExpoNat NoExpoNat
{-# RULES "fold/build" forall f n (g :: forall b. (Maybe a -> b -> b) -> b -> b). fold f n (build g) = g f n #-}
例如,我们从“Is there a way to generalize this TrieMap code?”中提取match 和appl 并将它们组合成ExpoNat 被融合掉。 (注意我们必须在appl中“强化归纳假设”。)
{-# INLINE match #-}
match :: Nat -> ExpoNat ()
match n = build $ \f z ->
let go Zero = f (Just ()) z
go (Succ n) = f Nothing (go n)
in go n
{-# INLINE appl #-}
appl :: ExpoNat a -> (Nat -> Maybe a)
appl
= fold (\f z -> \n ->
case n of Zero -> f
Succ n' -> z n')
(\n -> Nothing)
applmatch :: Nat -> Nat -> Maybe ()
applmatch x = appl (match x)
可以通过-ddump-simpl检查Core来验证融合。
现在我们想对 Tree 做同样的事情。
data Tree = Leaf | Node Tree Tree
data TreeMap a
= TreeMap {
tm_leaf :: Maybe a,
tm_node :: TreeMap (TreeMap a)
}
| EmptyTreeMap
我们遇到了麻烦:TreeMap 是一种非常规数据类型,因此如何编写其对应的折叠/构建对并不明显。
Haskell Programming with Nested Types: A Principled Approach 似乎有答案(请参阅Bush 类型),但凌晨 4:30 对我来说似乎为时已晚。一个人应该怎么写hfmap?自那以后有进一步的发展吗?
在What's the type of a catamorphism (fold) for non-regular recursive types? 中提出了这个问题的类似变体
【问题讨论】:
标签: haskell