Haskell 从'mappend'函数到'Monoid'实例的函数?
我有一个数据结构(它是rose树的一个特定子类,形成了一个具有最大下界和最低上界函数的晶格),并且它支持两个完全合理的函数作为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让我动态创建实例,但我知道这与我所理解的股票类型系统不一致。 如果我只需要选择一个默认的合并函数并为其
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
实例身份证名称。