作者macbuntu (邀怪)
看板PLT
標題Re: [問題] Scala 的 Covariant/Contravariant/Inv …
時間Mon Mar 16 00:34:09 2009
※ 引述《godfat (godfat 真常)》之銘言:
: 可以改看 Function 的例子,那就真的是有 contravariant parameter type 了。
: http://www.scala-lang.org/docu/files/api/scala/Function1.html
: trait Function1[-T1, +R]
: extends AnyRef
: 其中 -T1 是第一個 parameter, +R 是其 return type.
今天再玩了一下 Scala, 發現 Scala 的 variant 並沒有我原先想的那麼天真,
它有個很聰明的設計, 靠 compile time type checking 把不正當的型別使用擋掉,
所以我原先想的那些 covariant 跟 contravariant 的問題其實在 Scala 都不會發生.
真強... 譬如說:
class Foo[
T] (init:
T) {
private var x:
T = init
def get:
T = { x }
def set(x:
T) = { this.x = x }
}
這裡定義 Class Foo 用 Invariant type parameter, 所以沒問題,
不論 A < B 或 B < A, Foo[A] 跟 Foo[B] 都沒關係.
但如果換成用 +T 的 covariant 來定義 Foo:
class Foo[
+T] (init:
T) {
private var
x:T = init
def get:
T = { x }
def set(
x:T) = { this.x = x }
}
// convariant type T occurs in contravariant position ....
Scala 的 compiler 就會把上面紅色的地方標成錯誤而不會 compile 成功,
原因是 Scala 會根據型別出現的位置來決定該位置所能允許的 variant 種類,
所以像 function argument type 是 contravariant 而 return type 是 covariant
這類的規則直接定義成語言的一部分, 再將 programmer 宣告的 +/- variant 方式
跟這些位置作比對, 符合才放行, 從根本上解決了 variant 跟 OO 的衝突問題.
覺得這種方法挺漂亮的, 其實可以加到 Java 的語法裡又不會造成衝突.
以現在的 Java 來說:
interface
A<T> {
public
T get();
}
class B {
static void func(
A<Object> a) { .... }
}
A<String> obj = /* new something */;
B.func(
obj); // compile time error, A<String> is not an A<Object>
這裡 Java compiler 不允許, 但是仔細看 A<T> 的 T 其實只用在 return type,
把 A<String> 當作 A<Object> 使用一點問題也沒有. 如果把 Scala 的設計加進去:
interface
A<+T> {
public
T get(); // OK
}
interface
B<+T> {
public
T get(); // OK
public void set(
T t); // compile time error
}
interface
C<-T> {
public void set(
T t); // OK
}
interface
D<-T> {
public
T get(); // compile time error
public void set(
T t); // OK
}
interface
E<T> {
public
T get(); // OK
public void set(
T t); // OK
}
靠 compiler 來檢查所有定義為 +T (covariant) 的型別只能用在 return type,
而所有 -T (contravariant) 的型別只能用在 arguments type,
如果同時用在兩個地方, 或是 public field type, 就一定得指定成 T (invariant),
這樣就皆大歡喜了.
或者可以連 +/- 符號都不必放, 直接把上面想法當成 implicit sementic rule,
compiler 根據 T 出現的位置來認定它的 variant 能力, 然後才決定
Foo<String> 跟 Foo<Object> 之間有沒有繼承的關係, 這樣連 syntax 都不必改了.
話說回來, 能讓 programmer 明確指定意圖, 再讓 compiler 檢查正確性,
可能還是比 implicit rule 好吧.
印象中現在 Java 1.5 generics type 的設計就是出自於設計 Scala 的同一人?
不知他當時沒把這個放入 Java 中是有什麼原因? 還是當初還沒有這個點子...
: 雖然說冷了很久很久了(PLT 板也是),不過 function programming 是
: 最熱門的話題喲。 http://flolac.iis.sinica.edu.tw/lambdawan/forum/22
我只有很以前有用過 Lisp 而已, 現在只剩一個感覺就是寫起來像在玩頭腦體操 :P
看論文好像常常會看到 Haskell, 還真該找時間學學...
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 114.32.132.21