猫的 NonEmptyList 与 scala stdlib ::

cats' NonEmptyList vs scala stdlib ::

最近在研究cats库,遇到这个class叫NonEmptyList

阅读 api 后,我不禁想知道是什么让猫作者创建了一个新的 class,而不是利用内置的东西(::) 并使用类型 classes 来扩展它。它甚至没有在 cats github 页面中列出,所以我来这里询问。也许是因为 cons 是 List 的子类型? (虽然我不知道它的含义)

::NEL 有什么区别?为什么猫作者必须写 NEL 而不是使用 ::

NonEmptyList 不从 List 延伸的主要原因是 开发人员经验 在 API 中包含假设.

首先,请注意 :: 具有 List 所具有的所有方法,这些方法可能会产生误导,这使得使用更强大的假设设计更好的 API 变得更加困难。此外,List 没有任何直接 return :: 的方法,这意味着开发人员需要手动维护 非空 抽象。

让我给你举个例子来说明我在实践中的意思:

// NonEmptyList usage is intuitive and types fit together nicely
val nonEmpty: NonEmptyList[Int] = NonEmptyList.of(1, 2, 3)
val biggerNonEmpty: NonEmptyList[Int] = 0 :: nonEmpty
val nonEmptyMapped: NonEmptyList[Int] = nonEmpty.map(_ * 2)

// :: has lots of problems
// PROBLEM: we can't easily instantiate ::
val cons: ::[Int] = 1 :: 2 :: 3 :: Nil // type mismatch; found: List[Int]; required: ::[Int]
val cons: ::[Int] = new ::[Int](1, ::(2, ::(3, Nil)))

// PROBLEM: adding new element to Cons returns List
val biggerCons: ::[Int] = 0 :: cons // type mismatch; found: List[Int]; required: ::[Int]

// PROBLEM: ::.map returns List
val consMapped : ::[Int] = cons.map(_ * 2) // type mismatch; found: List[Int]; required: ::[Int] 

请注意,NonEmptyList 具有 return List 的方法,即 filterfilterNotcollect。为什么?因为通过 NonEmptyList 过滤可能意味着你过滤掉了所有元素,列表可以变成一个空列表。

这就是整个 非空 抽象如此强大的原因。通过正确使用函数输入和输出类型,您可以编码关于 API 的假设。 :: 不提供这种抽象。