我有一个数据结构(它是玫瑰树的特定子类,形成一个具有最大下限和最低上限函数的格子),它支持两个完全合理的函数作为{{ 1}} class' s Monoid
。
有没有办法支持haskell中的匿名mappend
个实例?这是一个我应该考虑使用像Template-Haskell之类的东西为我生成类型类的实例吗?
我喜欢的是Monoid
让我即时创建实例,但我理解这与我理解的库存类型系统不一致。
如果我只需要选择一个默认的合并函数并为其他合并编写makeMonoid :: (RT a -> RT a -> RT a) -> Monoid a
s,我就可以了,只是好奇
答案 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
中的幻像类型相同。