我将使用像monads这样的语言不可知的描述,首先描述monoid:
甲独异是(大约)的一组需要一些类型作为参数并返回相同的类型的功能。
甲单子是(大约)的一组需要的功能封装器类型作为参数并返回相同的包装类型。
注意这些是描述,而不是定义。随意攻击该描述!
因此,在OO语言中,monad允许以下操作组合:
Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
请注意,monad定义并控制这些操作的语义,而不是所包含的类。
传统上,在OO语言中,我们将使用类层次结构和继承来提供这些语义。因此,我们将有一个Bird
带有方法takeOff()
,flyAround()
和的类land()
,而Duck将继承这些方法。
但是后来我们遇到了不能飞的鸟,因为penguin.takeOff()
失败了。我们必须求助于异常抛出和处理。
另外,一旦我们说企鹅是a Bird
,我们就会遇到多重继承问题,例如,如果我们也有的层次结构Swimmer
。
本质上,我们试图将类归类(对“分类论”的人道歉),并按类别而不是单个类来定义语义。但是,与层次结构相比,monad似乎是一种更清晰的机制。
因此,在这种情况下,我们将有一个Flier<T>
单子,如上面的示例:
Flier<Duck> m = new Flier<Duck>(duck).takeOff().flyAround().land()
...而且我们永远不会实例化一个Flier<Penguin>
。我们甚至可以使用静态类型来防止这种情况的发生,也许使用标记接口。或运行时功能检查以纾困。但是,实际上,程序员切勿将企鹅放入Flier,从同样的意义上讲,它们切勿除以零。
而且,它更普遍适用。飞行器不必一定是鸟。例如Flier<Pterodactyl>
或Flier<Squirrel>
,而不更改这些单个类型的语义。
一旦我们通过容器上的可组合函数对语义进行了分类(而不是按类型层次结构进行分类),它就解决了“某种行为,某种行为不适合”特定类的旧问题。它还轻松明确地为一个类提供了多种语义,例如Flier<Duck>
和Swimmer<Duck>
。似乎我们一直在通过按类层次结构对行为进行分类来应对阻抗不匹配的问题。Monad优雅地处理它。
因此,我的问题是,就像我们倾向于偏爱组成而非继承一样,偏爱单子而不是继承是否也有意义?
(顺便说一句,我不确定是应该在这里还是在Comp Sci中使用,但这似乎更像是一个实际的建模问题。但是也许在那儿会更好。)