@
PTLin 多几种其实不是直接问题。上古的 cons pair 限制个别元素就能造出 list monad ,用比较现代一点的说法,决定限制的 unit predicate 是 null?;中古一点的 string 同样也是个 monad ,unit pred 是 string-null? 。其实吧,传统数学上 list 和 string 可以就是一回事。那为什么不嫌弃 string 和 list 共存冗余呢?因为 string 好歹有复杂度 hint 表明适合不同场景(激进一点的还有 encoding 甚至 SSO 之类的假设,但把本属于 text 的东西混同 string 有过度设计的问题,这又是另一回事了)。这个意义上 string 是具有更多实现假设的 list 又确实不都能替代 string ,这种不同层次上的冗余适应新的需求,其实不难接受。
问题是引入的目的和过程。新增的抽象如果没有提供更详细的假设提升到接口的含义,而仅仅是为了给不完善的旧有抽象擦屁股,但被擦屁股的东西又因为兼容性之类的原因没法被彻底替代而只能共存,这样缝合的下场自然不会让用户舒服了。
为什么说 null 是多余的呢?因为 nullable type 历史上本来就是实现偷懒( null pointer )的结果上反向臆造的抽象。要从需求侧讲,没什么和 Maybe 区分的必要,反倒是传统实现习惯(比如死板的错误处理)导致使用者体验很尴尬。相比之下,C++一样也有 optional<T&>不能随便和 T*等价的历史包袱,这里思想包袱就体现得更明显,因为 nullptr 并没有 Java 一样被迫弄得到处都是的存在感,兼容性问题本该不是借口。