我有一个数据类型(以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
的终端案例
有什么想法吗?
答案 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
对应于您似乎正在寻找的操作。