我正在尝试以不同的方式重写简单的函数,但我显然误解了一些核心概念。有没有更好的方法来处理像这样的有限类型?
mlength :: Monoid m => m -> Int
mlength mempty = 0
mlength (l <> r) = mlength l + mlength r
Run Code Online (Sandbox Code Playgroud)
编译失败并出现以下错误:
Parse error in pattern: l <> r
Run Code Online (Sandbox Code Playgroud)
我可以看到我的用法<>
是错误的,因为有多个正确的匹配l
和r
。尽管看起来分配哪个值并不重要,但最终仍然必须分配一个值。也许有一种方法可以让我为特定的 Monoid 实例断言这个决定?
"ab" == "" <> "ab"
"ab" == "a" <> "b"
"ab" == "ab" <> ""
Run Code Online (Sandbox Code Playgroud)
chi*_*chi 12
在一般情况下,幺半群没有长度的概念。以 为例Sum Int
,Int
它的幺半群运算配备了加法。我们有
Sum 3 <> Sum 4 = Sum 7 = Sum (-100) <> Sum 7 <> Sum (100)
Run Code Online (Sandbox Code Playgroud)
它的“长度”应该是多少?这里没有真正的长度概念,因为底层类型是Int
,它不是类似列表的类型。
又如:Endo Int
其中Int -> Int
装有构图。例如
Endo (\x -> x+1) <> Endo (\x -> x*2) = Endo (\x -> 2*x+1)
Run Code Online (Sandbox Code Playgroud)
同样,这里不能定义有意义的“长度”。
您可以浏览Data.Monoid
并查看没有“长度”概念的其他示例。
Const a
也是一个没有长度的(无聊的)幺半群。
现在,列表确实[a]
形成了幺半群(在 上的自由幺半群a
),并且确实可以在那里定义长度。不过,这只是一个特例,不能一概而论。
在Semigroup
与Monoid
接口提供建立价值观的一种手段,(<>)
。然而,它们并没有给我们一种方法来分解或以其他方式从值中提取信息。既然如此,length
超越某种特定类型的泛化需要不同的抽象。
正如在评论中讨论志的回答,同时Data.Foldable
提供一个广义的length :: Foldable t => t a -> Int
,它是你在被瞄准不太什么-特别之间的连接Foldable
,并Monoid
是可折叠的结构可以转换为列表/自由幺半群,并不算可折叠本身必然是幺半群。
另一种可能有点晦涩但更接近您的问题的精神,是Factorial
来自monoid-subclasses包的类,Semigroup
. 它是围绕 构建的factors :: Factorial m => m -> [m]
,它将一个值拆分为不可约的因素,撤消什么sconcat
或mconcat
做什么。length :: Factorial m => m -> Int
然后可以将广义定义为因素列表的长度。无论如何,请注意,我们最终仍然需要在Semigroup
/之上进一步抽象Monoid
。