如何映射ADT的构造函数的参数?

时间:2016-12-20 05:24:48

标签: haskell

我有一个数据类型(以GADT形式显示如下):

data Test a where
  C1 :: Int -> a -> Test a
  C2 :: Test a -> Test a -> a -> Test a
  C3 :: Test a -> a -> Test a
  ...

我想要做的是,能够将一些函数Monoid m => Test a -> m一般性地应用于构造函数中Test a的任何给定实例,然后mappend全部。

例如,给定f:

f :: Test a -> [Int]
f (C1 n _) = [n]
f _        = []

我喜欢一些函数g,可以在每个构造函数参数上映射它,如下所示:

g :: Monoid m => (Test a -> m) -> Test a -> m
g f v@(C1 Int _) = f v
g f (C2 x y _)   = f x `mappend` f y
g f (C3 x _)     = f x
...

除了我不想写g之外,我想写一个g的通用版本,可以为任何支持的数据类型执行此操作(我正在思考) GHC.Generics可能非常适合这种情况,但是还没有能够获得正确的类型)。 即我想从有趣的位中分离遍历我的数据结构的实际机制(f与基于mappend的折叠的重复应用) C1以上g的终端案例

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

实际上,我对GHC.Generics有了更深入的了解,如果你还没有,请务必阅读the SYB paper

我将在此处概述另一种方法,即使用修复点类型,这非常适合此问题。

PatternSynonyms

在实践中,我实际上并没有使用修复点类型,它们似乎总是比它们的价值更麻烦。但是现在实现了redirect_to user_path(current_user.id) ,我认为它有点好。无论如何,只是想向您展示这一点供您考虑。

答案 1 :(得分:0)

看起来你想要做的事情或多或少正是函数composFold所做的,正如论文"A pattern for almost compositional functions"(第3节)所述。

uniplate包声称拥有compatibility layer for compos-like operations

composOpMonoid :: (Uniplate a, Monoid m) => (a -> m) -> a -> m

对应于您似乎正在寻找的操作。