Haskell-ish 对这个问题的看法是观察以下操作:
max, min :: Ord a => a -> a -> a
max a b = if a < b then b else a
min a b = if a < b then a else b
...是关联的:
max a (max b c) == max (max a b) c
min a (min b c) == min (min a b) c
因此,任何类型Ord a => a
与这些操作中的任何一个一起都是semigroup,这是一个可以构建可重用抽象的概念。
而且您正在处理Maybe
(Haskell for “option”),它向基本类型添加了一个通用的“中性”元素a
(您希望max Nothing x == x
将其保留为法律)。这将带您进入monoids,它是半群的子类型。
Haskellsemigroups
库提供了一个Semigroup
类型类和两个包装器类型,Max
以及Min
,它们通常实现相应的行为。
由于我们正在处理Maybe
,就该库而言,捕获您想要的语义的类型是Option (Max a)
- 一个与半群具有相同二元运算的Max
幺半群,并Nothing
用作标识元素。那么函数就变成了:
maxOpt :: Ord a => Option (Max a) -> Option (Max a) -> Option (Max a)
maxOpt a b = a <> b
...因为它只是<>
运算符 forOption (Max a)
不值得写。Semigroup
您还可以获得在 and 上工作的所有其他实用函数和类,Monoid
例如,要找到 a 的最大元素,[Option (Max a)]
您只需使用mconcat
function。
scalaz 库带有 aSemigroup
和 a特征,Monoid
以及实现这些特征的Max
、和标记Min
MaxVal
MinVal
,因此实际上我在 Haskell 中演示的内容也存在于 scalaz 中。