What have you found for these years?

2009-03-15

Re: Scala 的 Covariant/Contra-/In-

避免轉貼問題,原文我都剪掉了,看原文請去原文章看。
┌─────────────────────────────────────┐
│ 文章代碼(AID): #19lAo3ED (PLT) [ptt.cc] Re: [問題] Scala 的 Covariant/Co │
│ 文章網址: http://www.ptt.cc/bbs/PLT/M.1237101699.A.38D.html
│ 這一篇文章值 593 銀 │
└─────────────────────────────────────┘
作者 godfat (godfat 真常) 看板 PLT
標題 Re: [問題] Scala 的 Covariant/Contravariant/Inv …
時間 Sun Mar 15 15:21:36 2009
───────────────────────────────────────

※ 引述《macbuntu (邀怪)》之銘言:

在這邊引用 Java 的 array 的例子,應該就算是一個不良應用吧,哈哈 XD
在 Scala 裡,Array[A] 是 invariant 的,不是 covariant 的。
http://www.scala-lang.org/docu/files/api/scala/Array.html

然而 List[+A] 是 covariant, 但會有這個問題嗎?答案應該是不會。
因為 List 其實是 constant, 根本就沒有 add 這種會改變 state 的操作。
因此任你隨意去 point/refer, 不會使得容器被存入錯誤的東西。
也就是說以下:

: String[] s = new String[5];
: Object[] o = s; // array is covariant so this is allowed
: o[3] = new Object();
^^^^^^ 這件事本身是沒辦法發生在 Scala 的 List 上,
而 Array[A] 則是 invaraint 的。
: String name = s[3]; // throws ArrayStoreException !
所以絕不會產生這種,我覺得有點可笑的 exception...

用 Java 的角度去看,我想確實是很難找到需要 contravariant 的時候。
然而可以看這裡:

http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

我們知道 overriding method 可以有 covariant return type,
但是 parameter type 呢?事實上卻是相反,是 contravariant 的。
用同樣的概念,class pointer/reference 可以指向 subclass instance,
所以 parameter 應該也可以用 class pointer/reference 指向 subclass instance.
以 Java 為例的話:(例子隨便舉的啦 @@)

class Human{
public Human touch(Mutant m){
return m;
}
}

class Mutant extends Human{
@Override
public Mutant touch(Human h){
// ^ OK, covariant return type
// ^ OK, contravariant parameter type.
return this;
}
}

不過這只是個美好的夢,Java 不支援的樣子 XD
C++ 也不支援,甚至 Scala 自己也不支援...

這邊可以想像成,在 Mutant#touch 裡面,用 Human h 去接
Human#touch 裡的 Mutant m, 應該不會產生什麼不良的操作。
但是 Scala 自己也不支援,或許是因為怕產生一些混亂吧。

可以改看 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.
一樣的 Human/Mutant 例子:

class Human;
class Mutant extends Human;

// 定義變種,把正常人類轉換成變種人。
// apply 是 mutate 的 method, 當我們寫 mutate(h) 時會喚起。
val mutate = new Function1[Human, Mutant]{
def apply(h: Human) = new Mutant;
}

// 定義治療,可以把變種人類轉換回正常人類。
val false_heal: Function1[Mutant, Human] = mutate

// 定義我,希望可以從變種人轉換回正常人類。
val me: Human = false_heal(new Mutant)

這邊實際上 argument 的套用,就是把 new mutant 丟給 apply 的 h,
而 me 雖然是 Human, 但可以指向 Mutant.

可以想像成 +A 是允許 type 往上層移動,而 -A 則是允許 type 往下層移動。
因此這邊也是可以寫:

val create: Function1[Null, Any] = mutate
val any: Any = create(null)

這就是極端的例子,丟最底層的 null 進去,而拿到一個最上層的 any 回來。
在 Java 裡,就像是 Object any = mutate(null); 這樣。

*

Java 確實是非常保守,實在不是我能夠喜歡的語言,哈哈 XD
而 Scala 也確實是放了一大堆東西進去,多到我覺得有點不可思議...
這一兩天為了寫這幾篇,翻了點 Scala 的東西,開始覺得還滿有趣的 :p
只是他的語法實在有點多變,keyword 也一大堆,要適應可能要一點時間。

至於 functional programming 嘛,看到現在還是覺得 Haskell 最乾淨漂亮。
歡迎來這裡逛逛:

http://flolac.iis.sinica.edu.tw/lambdawan/

雖然說冷了很久很久了(PLT 板也是),不過 function programming 是
最熱門的話題喲。 http://flolac.iis.sinica.edu.tw/lambdawan/forum/22

--
Nobody can take anything away from him.
Nor can anyone give anything to him.

What came from the sea,
has returned to the sea.
Chrono Cross

--
※ 發信站: 批踢踢實業坊(ptt.cc)

0 retries:

Post a Comment

Note: Only a member of this blog may post a comment.



All texts are licensed under CC Attribution 3.0