我正在尝试以不同的方式重写简单的函数,但我显然误解了一些核心概念。有没有更好的方法来处理像这样的有限类型?
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。