Haskell 从'mappend'函数到'Monoid'实例的函数?

Haskell 从'mappend'函数到'Monoid'实例的函数?,haskell,types,metaprogramming,Haskell,Types,Metaprogramming,我有一个数据结构(它是rose树的一个特定子类,形成了一个具有最大下界和最低上界函数的晶格),并且它支持两个完全合理的函数作为Monoid类的mappend 在haskell中有没有办法支持匿名Monoid实例?这是一个实例,我应该考虑使用像模板Haskell之类的东西来为我生成我的类型吗?< /P> 我喜欢的是一个makeMonoid::(RT a->RT a->RT a)->Monoid a让我动态创建实例,但我知道这与我所理解的股票类型系统不一致。 如果我只需要选择一个默认的合并函数并为其

我有一个数据结构(它是rose树的一个特定子类,形成了一个具有最大下界和最低上界函数的晶格),并且它支持两个完全合理的函数作为
Monoid
类的
mappend

在haskell中有没有办法支持匿名
Monoid
实例?这是一个实例,我应该考虑使用像模板Haskell之类的东西来为我生成我的类型吗?< /P> 我喜欢的是一个
makeMonoid::(RT a->RT a->RT a)->Monoid a
让我动态创建实例,但我知道这与我所理解的股票类型系统不一致。 如果我只需要选择一个默认的合并函数并为其他合并编写
newtype
s,我可以接受它,只是好奇您可以使用包中的工具动态创建
Monoid
的“本地”实例。存储库中有一个。这个答案解释了一点

这是一个新的类型包装器,覆盖
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 }
以下是
M
Monoid
实例定义:

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\uuu
的类型级表示,我们就可以将其反射回来以获取字典,并使用字段来实现
M
Monoid
操作。”

请注意,传递给的实际值
a
未被使用,它仅作为
M a s
类型的“代理”传递,该代理告诉
反射
用于“带回记录”的类型(
s

实际的本地实例是使用以下函数构造的:

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
函数是一个让编译器确信monoid中使用的幻影类型与
reify

提供的
Proxy
中使用的幻影类型相同的技巧,如果您有多个实例,并且不想在任何地方都使用
newtype
作为
数据RTMonoid a={empty::RT a,append::RT a->RT a->RT a}
rt1::RTMonoid a;rt1=RTMonoid…
rt2::RTMonoid a;rt2=RTMonoid…
,然后您可以编写函数来接受您使用的
RTMonoid a
参数。手动传递它会有点麻烦,但它会使它更具可扩展性。如果您只有2个实例,请使用新实例类型。您根本不必选择默认类型。强制用户选择使用新类型可能更好。您还可以使用单独的函数来
mergeThisWay
mergeThisWay
。您可以亲自使用类型类,我希望格在Haskell中使用得更频繁。它们是一种非常有用的结构。例如f如何使用
和monoid
将大大增加关于
数据的介绍性文献。Reflection
@Cirdec另一个很好的例子是根据运行时获得的信息,动态生成一个类型的
FromJSON
/
到JSON
实例身份证名称。