我具有以下类型,希望成为Monoid
类型类的实例。我不知道如何为身份设置参数化字段。使用参数化类型获取身份时是否有任何方法那个类型?
data Tree a=Leaf a | Node a (Tree a) (Tree a) |Empty deriving (Eq,Show)
instance Monoid Tree where
mempty=Empty
mappend a Empty=a
mappend a b=Node (identity) x y
如您所见,我需要将简单字段设置为参数类型的标识。
示例
mappend::Tree Int
mappend (Leaf 1) (Leaf 2)=Node 0 (Leaf 1) (Leaf 2)
mappend::Tree []
mappend (Leaf [1,2])(Leaf [2,3])=Node [] (Leaf [1,2])(Leaf [2,3])
答案 0 :(得分:9)
仅当a
本身也是Monoid
类型时,才会发生这种情况,因此我们可以这样写:
instance Monoid a => Monoid (Tree a) where
mempty = Empty
mappend Empty a = a
mappend a Empty = a
mappend a b = Node mempty a b
以上内容不适用于Int
,因为Int
不是Monoid
。有两个非常受欢迎的候选项(ℕ,+,0)和(ℕ,×,1)。但是,您可以使用Sum
,它是前半体动物的表示。
最后一行正文中的mempty
不是我们定义的mempty
,而是mempty
类型的a
话虽如此,如果您这样定义Monoid Tree
,则意味着您考虑使用Node mempty (Node mempty a b) c == Node mempty a (Node mempty b c)
,因为这是 monoid定律所必需的。因此,deriving Eq
与mappend
并不完全处于“ harmony ”。 mappend
运算符(在数学中通常表示为⊕)应满足条件∀a,b,c∈S:a⊕(b⊕c)=(a⊕ b)⊕c。
您应该以不同的方式实现Eq
自己,或者应该尝试为自己的半定式提出另一种结构。
答案 1 :(得分:2)
这不是答案,但是评论太久了。正如Willem Van Onsem所建议的那样,您的Monoid
实例是非法的。我怀疑您可能想要两件事之一:
可以定义一种类型的自由岩浆
data Magma a = Branch (Magma a) (Magma a) | Leaf a | Empty
deriving Show
这不是天生的类人动物,但假装有时是有用的。
instance Monoid (Magma a) where
mempty = Empty
mappend = Branch
-- For recent GHC, add this instance
instance Semigroup (Magma a) where
(<>) = Branch
此功能可与折叠一起使用,以深入了解该折叠的结构。为了了解其工作原理,请比较将foldMap Leaf
应用于[1..20]
,Data.Sequence.fromList [1..20]
和Data.Set.fromList [1..20]
的结果。
考虑对树进行泛化,以允许内部节点中的叶子类型不同于叶子中的类型:
data Tree a b = Node a (Tree a b) (Tree a b) | Leaf b
deriving (Show, Eq)
(这是定义的函子Tree a
上的免费monad
data TreeF a b = NodeF a b b
但我们实际上并不需要深入了解这意味着什么。
Tree a
monad的monad操作是一种“嫁接”,其中叶子被树替换。
instance Functor (Tree a) where
fmap f (Leaf b) = Leaf (f b)
fmap f (Node a l r) = Node a (fmap f l) (fmap f r)
instance Applicative (Tree a) where
pure = Leaf
(<*>) = ap
liftA2 = liftM2
instance Monad (Tree a) where
Leaf b >>= f = f b
Node a l r >>= f = Node a (l >>= f) (r >>= f)
您可以认为>>=
支持某种垂直附加,即树从其叶子向下生长。