从`mappend`函数到`Monoid`实例的函数?

时间:2015-02-21 18:49:01

标签: haskell types metaprogramming

我有一个数据结构(它是玫瑰树的特定子类,形成一个具有最大下限和最低上限函数的格子),它支持两个完全合理的函数作为{{ 1}} class' s Monoid

有没有办法支持haskell中的匿名mappend个实例?这是一个我应该考虑使用像Template-Haskell之类的东西为我生成类型类的实例吗?

我喜欢的是Monoid让我即时创建实例,但我理解这与我理解的库存类型系统不一致。 如果我只需要选择一个默认的合并函数并为其他合并编写makeMonoid :: (RT a -> RT a -> RT a) -> Monoid a s,我就可以了,只是好奇

1 个答案:

答案 0 :(得分:2)

可以使用reflection包中的工具随时创建Monoid的“本地”实例。存储库中有一个ready-made example。这个答案解释了一下。

这是类型为a的值的新类型包装器,我们将在其上定义Monoid实例。

newtype M a s = M { runM :: a } deriving (Eq,Ord)

请注意,幻影类型s未显示在右侧。它将携带本地Monoid实例工作所需的额外信息。

这是一个记录,其字段代表Monoid类的两个操作:

data Monoid_ a = Monoid_ { mappend_ :: a -> a -> a, mempty_ :: a }

以下是Monoid的{​​{1}}实例定义:

M

它说:“只要instance Reifies s (Monoid_ a) => Monoid (M a s) where mappend a b = M $ mappend_ (reflect a) (runM a) (runM b) mempty = a where a = M $ mempty_ (reflect a) s字典Monoid的类型级别表示,我们就可以反映它以获取字典,并使用字段来实现Monoid_的{​​{1}}次操作。

请注意,未使用传递给reflect的实际值Monoid,它仅作为M类型的“代理”传递,告知a哪种类型( M a s)用来“带回记录”。

使用reify函数构建实际的本地实例:

reflect

s函数是一种技巧,可以说服编译器在monoid中使用的幻像类型与withMonoid :: (a -> a -> a) -> a -> (forall s. Reifies s (Monoid_ a) => M a s) -> a withMonoid f z v = reify (Monoid_ f z) (runM . asProxyOf v) asProxyOf :: f s -> Proxy s -> f s asProxyOf a _ = a 提供的asProxyOf中的幻像类型相同。