What have you found for these years?

2011-01-29

是 parent 還是 child?

Aaron Patterson (author of nokogiri, psych, etc.) 在 ruby-core 上
(其實是 ruby 的 issue tracker, 只是會 mirror 至 ruby-core)
提出了一個有趣的問題。他說:
Knuth says "Subroutines are special cases of ... coroutines".
This makes sense to me.
有趣的地方在於,如果 A is a special case of B,
那麼對應到 object-oriented 裡面,到底誰是 parent 誰是 child?

昨天看到時,其實我沒有想太多,因為我不熟悉 coroutine,
也不在意 Fiber 是否應該具備 [] 與 call 這兩個 method,
心裡在想的只有 why not? 乍看似乎沒什麼不好。

然而看到 Charles Nutter (author of jruby, mirah, etc.) 所說的:
If we go by Knuth, a subroutine "is a" coroutine
我忽然就開始想了,這似乎不怎麼正確?就自然語言而言,
當我們說 A is a special case of B, 那麼很自然會推斷
A is a kind of B. 白馬是馬的特例,白馬是一種馬,
這看起來再自然也不過了。但事實上,套用在物件導向中,
並不永遠是這回事... 自然語言在這句話上,似乎把很多種
概念混合在一起了。硬要套用的話,很可能會違反 LSP.

雖然 LSP 是個到處都被違反的原則,但我仍然覺得盡可能
不要違反 LSP, 就算表面上看起來很詭異也不要違反 LSP,
因為一旦違反 LSP, 程式就有可能產生非預期的行為,
而這則是建基於 subtype polymorphism.

根據這個原則,parent 能做的所有事,child 都一定要能做,
而 child 可以做更多,也可以不做更多,但不能拒絕不做
parent 會做的事情。

現在回過頭來看,A is a special case of B, 那誰是 parent,
誰是 child? 乍看之下這句話就是 A is a kind of B, 因此很自然
parent 是 B, 而 child 是 A. 可是 a special case 真的是這個意思嗎?
在自然語言中,似乎就算並非包含關係,也會用上這句話。

Jim Weirich (author of rake) 又舉了那個人盡皆知的例子:
square is a special case of rectangle. 而事實上,其實也不見得
這表示 square 和 rectangle 上不能具備 subtype 的關係。
畢竟在某個 domain 裡面,比方說完全不需要 set_width/height,
那麼他們之間即使確實有 is-a 的關係,無論哪個方向,
似乎也不會違反 LSP.

*

再換個角度想,subtype 與 supertype 之間,通常是怎麼寫的?
subtype < supertype
因為 supertype 是 generalized, 而 subtype 是 specialized,
能夠是 supertype 的 value 會比能夠是 subtype 的 value 要來得多。
因此這邊 subtype < supertype 是 type (set) 本身的比較。

然而如果我們有實際的 value, 令 A, B 是 type, 且 A < B,
而 a 是 A 的 instance, b 是 B 的 instance, 則 b <= a,
因為 a (child) 能做的事情比 b (parent) 要來得多,
也就是說 value 跟 type 其實反而應該反過來看。

自然語言,或說口語上,其實不會特別去區分 type 跟 value,
因此有時候反而應該反過來看...

在 scala 中,Any (top) 可以指向任何東西(很大),然而一個
Any 的 instance 卻什麼也不能做(很小)。事實上因為 Any 是
abstract, 也不能擁有一個 Any 的 instance. 如果是 RichInt,
則能指向的 instance 變少了,但擁有一個 RichInt 能做的事卻
變多了,所有其上的 supertype 所能做的事情,他都能做,
by definition:
If S is a subtype of T, the subtyping relation is often written S <: T,
to mean that any term of type S can be safely used in a context
where a term of type T is expected.
無言的是,scala 中的 Nothing (bottom) 和 Null (nearly bottom)
卻違反了 LSP 與這個原則,他們很明顯無法「安全地」取代任何 value.
(e.g. null pointer exception)

有趣的是,再繼續看下去:
The precise semantics of subtyping crucially depends on the
particulars of what "safely used in a context where" means in a
given programming language. The type system of a programming
language essentially defines its own subtyping relation, which
may well be trivial.
所以其實還是各自表態啦,違反 LSP 也沒什麼... XD
畢竟終究程式語言是給人用的,一兩個破例而讓工具更好用,
當然也是勢在必行的 (?)

0 retries:

Post a Comment

All texts are licensed under CC Attribution 3.0