如何为所有 Monoid 编写长度函数

cod*_*dle 8 haskell

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

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)

我可以看到我的用法<>是错误的,因为有多个正确的匹配lr。尽管看起来分配哪个值并不重要,但最终仍然必须分配一个值。也许有一种方法可以让我为特定的 Monoid 实例断言这个决定?

"ab" == ""   <> "ab" 
"ab" == "a"  <> "b" 
"ab" == "ab" <> ""
Run Code Online (Sandbox Code Playgroud)

chi*_*chi 12

在一般情况下,幺半群没有长度的概念。以 为例Sum IntInt它的幺半群运算配备了加法。我们有

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),并且确实可以在那里定义长度。不过,这只是一个特例,不能一概而论。

  • 嗯,它确实具有概括性,但方向完全不同——不是针对所有幺半群,而是针对所有“ta”,其中“t”是“可折叠”。并且幺半群是“可折叠”定义的关键,因此也许存在一些更深层次的联系(尽管,正如您所指出的,任意幺半群的“长度”函数实际上没有意义)。 (2认同)
  • @RobinZigmond 当然,但这是作弊(IMO),因为任何合法的 `Foldable t` 实例都是由它的 `toList :: ta -&gt; [a]` 唯一确定的。在我看来,说列表长度泛化到任何可折叠的东西,就相当于说列表长度泛化到任何可以转换成列表的东西,这并不奇怪。也许我对“可折叠”缺乏热情,这使我无法理解其概括性。 (2认同)

dup*_*ode 5

SemigroupMonoid接口提供建立价值观的一种手段,(<>)。然而,它们并没有给我们一种方法来分解或以其他方式从值中提取信息。既然如此,length超越某种特定类型的泛化需要不同的抽象。

正如在评论中讨论志的回答,同时Data.Foldable提供一个广义的length :: Foldable t => t a -> Int,它是你在被瞄准不太什么-特别之间的连接Foldable,并Monoid是可折叠的结构可以转换为列表/自由幺半群,并不算可折叠本身必然是幺半群。

另一种可能有点晦涩但更接近您的问题的精神,是Factorial来自monoid-subclasses的类,Semigroup. 它是围绕 构建的factors :: Factorial m => m -> [m],它将一个值拆分为不可约的因素,撤消什么sconcatmconcat做什么。length :: Factorial m => m -> Int然后可以将广义定义为因素列表的长度。无论如何,请注意,我们最终仍然需要在Semigroup/之上进一步抽象Monoid