Last active
July 7, 2024 09:01
-
-
Save rayshih/7b7bb9dd28fec505cc7bb64fa2bd3a3f to your computer and use it in GitHub Desktop.
Revisions
-
rayshih revised this gist
Jun 9, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -151,7 +151,7 @@ ConstList<? extends Animal> list = new ConstList<Cat>(); 當 generic type variable 只出現在 function input 的時候。 例如 `Comparator`,內含一個 function 可以比較兩個東西的大小, 如果前者比較大則輸出大於 0 的數值,如果一樣就輸出 0,比較小則輸出小於零的數值。 `Comparator<Animal>` 可以比較 Animal 的大小,而 `Comparator<Cat>` 可以比較 Cat 的大小。 然後我們就可以來問一樣的問題:`Comparator<Animal>` 是不是 `Comparator<Cat>` 的 subtype? -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -69,7 +69,7 @@ listOfAnimal.add(new Dog()); 也就是,你正在把 dog 塞進一個只能放 Cat 的 List,而如果其他地方還持有 `listOfCat` 的 reference 的話,就有可能拿到 dog, 而 dog 不是 cat,當然就是個 type error。 **也就是如果一個 list 允許你「塞」值進去的話,那它就不會是 subtype。** 既然 `List<Cat>` 不是 `List<Animal>` 的 subtype,那 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -141,12 +141,12 @@ ConstList<? extends Animal> list = new ConstList<Cat>(); ## 什麼是 Contravariance? > 假設 A 是 B 的 subtype,那麼 `Comparator<B>` 就是 `Comparator<A>` 的 subtype 那麼有沒有反過來「假設 A 是 B 的 subtype,那麼 `Comparator<B>` 就是 `Comparator<A>` 的 subtype」的呢? 有!它叫做 Contravariance。 當 generic type variable 只出現在 function input 的時候。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 4 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -241,7 +241,10 @@ boxedValue.cat.name = 'Cookie' 例如 Java 沒有提供語法可以在宣告 generic type 的時候描述 variance,TypeScript 也是類似狀況。 而 Swift 則沒有支援 variance 語法,奇怪的是 Objective C 有。 因為 generic 會讓 subtyping check 變複雜,如果不用繼承,就能減少 subtyping 造成的 variance 問題。 但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 希望這篇文章可以幫助你更好地理解 subtyping (繼承)而能更容易地設計出良好的架構。 最後謝謝 @wu_ct 跟 @\_cybai 的 review -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 15 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -94,6 +94,11 @@ Cat cat = listOfCat.get(0) // 取得 Animal ## 什麼是 Covariance? > 假設 A 是 B 的 subtype,那麼 `ConstList<A>` 就是 `ConstList<B>` 的 subtype 首先,在上述的例子中我們已經知道如果我們可以「塞」東西進去這個 List , 那就不可能做到「`List<Cat>` 是 `List<Animal>` 的 subtype」。 所以我們要另外定一個 list 叫做 ConstList,意思是這個 list 是 constant,也就是我們不能改動裡面的值。 @@ -133,11 +138,17 @@ ConstList<? extends Animal> list = new ConstList<Cat>(); 以上就是所謂的 Covariance:「假設 A 是 B 的 subtype,那麼 `ConstList<A>` 就是 `ConstList<B>` 的 subtype」 ## 什麼是 Contravariance? > 假設 A 是 B 的 subtype,那麼 `ConstList<B>` 就是 `ConstList<A>` 的 subtype 那麼有沒有反過來「假設 A 是 B 的 subtype,那麼 `ConstList<B>` 就是 `ConstList<A>` 的 subtype」的呢? 有!它叫做 Contravariance。 當 generic type variable 只出現在 function input 的時候。 例如 `Comparator`,內涵一個 function 可以比較兩個東西的大小, @@ -232,3 +243,5 @@ boxedValue.cat.name = 'Cookie' 如果不用繼承,就能減少 subtyping 造成的 variance 問題。但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 最後謝謝 @wu_ct 跟 @\_cybai 的 review -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 5 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -32,7 +32,11 @@ Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 比較好的方式應該是使用這個定義: > 如果 A 是 B 的 subtype(或 B 是 A 的 supertype),那 B 有的東西、能做的事情,都可以從 A 身上找得到。 例如 Cat 是 Animal 的 subtype,這個很明顯應該不用特別解釋。而 Animal 不是 Cat 的 subtype,其中一個原因是不是所有 Animal 都會喵貓叫。 -
rayshih revised this gist
Jun 8, 2020 . No changes.There are no files selected for viewing
-
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 3 additions and 3 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -156,8 +156,7 @@ animalComparator = catComparator; // 假設 Comparator<Cat> 是 Comparator<Anima animalComparator.compare(animal, animal); ``` 乍看之下沒問題,但實際上是拿 `Comparator<Cat>` 來比較 `Animal`,方式可能是比較喵喵叫的可愛程度,可是並不是所有 `Animal` 都會喵喵叫。 與 Covariance 相對應,他們分別的寫法是 @@ -224,7 +223,8 @@ boxedValue.cat.name = 'Cookie' ## 小結 在查找資料後才發現原來到今天為止,號稱 strong type 並有支援 generic 的語言有部分並沒有完善支援, 例如 Java 沒有提供語法可以在宣告 generic type 的時候描述 variance,TypeScript 也是類似狀況。 而 Swift 則沒有支援 variance 語法,奇怪的是 Objective C 有。 如果不用繼承,就能減少 subtyping 造成的 variance 問題。但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 55 additions and 9 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,7 +23,26 @@ Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 基本上, generic 的出現代表了 type system 增加了一個新的維度,碰上有 subtype 的 type system 會變得比平常複雜。 偏偏現代幾乎所有程式語言都有 inheritance,所以這變成了現在 programmer 早晚會碰到的問題。我聽到的 case 都幾乎是走 「碰到就想辦法繞過」這種解決方式。而個人在工作上也有碰到因為對 generic 一知半解而設計出來得詭異架構。要了解什麼是 covariance ,我們必須先了解 generic 碰上 subtype 會出現什麼樣的問題。不過首先,讓我們先複習一下什麼是 subtype。 # 什麼是 Subtype、Supertype 從 Java 派 OOP 的角度來說,如果 A 繼承 B ,我們稱 A 為 B 的 subtype,而 B 為 A 為 supertype。 但因為我們接下來要理解在 generic 的 context 下怎麼去理解 subtype,所以從語法來看並不是一個很泛用的方法。 比較好的方式應該是使用這個定義: 如果 A 是 B 的 subtype(或 B 是 A 的 supertype),那 B 有的東西、能做的事情,都可以從 A 身上找得到。 例如 Cat 是 Animal 的 subtype,這個很明顯應該不用特別解釋。而 Animal 不是 Cat 的 subtype,其中一個原因是不是所有 Animal 都會喵貓叫。 以下我們用這個 subtype 關係鏈來舉例: - 波斯貓 < Cat < Animal - Dog < Animal 現在我們對 subtype 有共識了,接下來我們可以回來看怎麼來解 generic type 的 subtype 關係。其中最經典的: ## `List<Cat>` 是不是 `List<Animal>` 的 subtype? @@ -46,7 +65,9 @@ listOfAnimal.add(new Dog()); 也就是,你正在把 dog 塞進一個只能放 Cat 的 List,而如果其他地方還持有 `listOfCat` 的 reference 的話,就有可能拿到 dog, 而 dog 不是 cat,當然就是個 type error。 **也就是如果一個 list 允許你「塞」值進去的話,那它就不是不會是 subtype。** 既然 `List<Cat>` 不是 `List<Animal>` 的 subtype,那 ## `List<Cat>` 能不能是 `List<Animal>` 的 supertype? @@ -63,7 +84,7 @@ Cat cat = listOfCat.get(0) // 取得 Animal 因為 `listOfCat` 如果是 `listOfAnimal` 的 supertype 的話,代表 `listOfCat` 有可能實際上是 `listOfAnimal`, 也就是你取到的值有可能是 `Animal` 而不是 `Cat`,而 `Animal` 並不是 `Cat` 的 subtype,於是這邊會產生另外一個 type error。 所以以常識上的通用 `List` 來說的話,`List<Cat>` **既不是** `List<Animal>` 的 **subtype 也不是 supertype。** 那假設我們想做一個特殊的 `List` 使得 `List<Cat>` 是 `List<Animal>` 的 subtype 呢? @@ -120,12 +141,25 @@ ConstList<? extends Animal> list = new ConstList<Cat>(); `Comparator<Animal>` 可以比較 Animal 的大小,而 `Comparator<Cat>` 可以比較 Cat 的大小。 然後我們就可以來問一樣的問題:`Comparator<Animal>` 是不是 `Comparator<Cat>` 的 subtype? 要來回答個問題,我們要來複習一下什麼是 subtype:「如果 A 是 B 的 subtype,那 B 能做到的事情 A 一定都能做到。」 `Comparator<Animal>` 可以比較 `Animal`,而因為 `Cat` 是 `Animal` ,所以 `Comparator<Animal>` 當然也可以比較 `Cat`。 所以 `Comparator<Animal>` 是 `Comparator<Cat>` 的 subtype。 光這樣講可能不是很清楚,讓我們先舉個反例,寫成 code 的話就是: ``` // FlowType let catComparator: Comparator<Cat> = // .... let animalComparator: Comparator<Animal> = // ... animalComparator = catComparator; // 假設 Comparator<Cat> 是 Comparator<Animal> 的 subtype animalComparator.compare(animal, animal); ``` 乍看之下沒問題,但實際上是拿 `Comparator<Cat>` 來比較 `Animal`,方式可能是比較毛量,可是並不是所有 `Animal` 都有毛啊。 與 Covariance 相對應,他們分別的寫法是 FlowType @@ -174,11 +208,23 @@ const d: { [string]: number } = c; 所以有些人會以為寫上 covariant 等於宣告了 immutable data type。的確在某些狀況下是可以這麼做的, 例如上面的 flowtype 練習題。但這並不全然是對的,因為它其實只描述了 subtyping 的關係,而不是 immutability。 那麼在什麼樣的情況下**不能**當 immutable 呢? ``` // FlowType type Cat = { name: string } const cat: Cat = { name: 'Kitty' } const boxedValue: { +cat: Cat } = { cat } boxedValue.cat.name = 'Cookie' ``` 其實跟 `const` (or `val` in Kotlin, Scala, etc) 一樣,他只限制了你不能改這個 reference, 但並沒有限制你不能改這個 value 裡面的內容。 ## 小結 在查找資料後才發現原來到今天為止,號稱 strong type 並有支援 generic 的語言有部分並沒有完善支援, 例如 Java 沒有提供語法可以在宣告 generic type 的時候描述 variance。Swift 則沒有支援 variance 語法,奇怪的是 Objective C 有。 如果不用繼承,就能減少 subtyping 造成的 variance 問題。但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 -
rayshih renamed this gist
Jun 8, 2020 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -176,7 +176,7 @@ const d: { [string]: number } = c; ## 小結 在查找資料後才發現原來到今天為止,號稱 strong type 並有支援 generic 的語言有部分並沒有完善支援,例如 TypeScript 的作法基本上就不檢查,直接讓你過(說好的 strict 呢?),而 Swift 則沒有支援 variance 語法,奇怪的是 Objective C 有。 如果不用繼承,就能減少 subtyping 造成的 variance 問題。但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 6 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -168,6 +168,12 @@ const d: { [string]: number } = c; 請問 function `Cat => void` 是不是 function `Animal => void` 的 subtype? ## 奇技淫巧 上面提到,如果可以「塞」東西進去的 generic type 就不能可能是 covariant,這看起來有點像 immutable, 所以有些人會以為寫上 covariant 等於宣告了 immutable data type。的確在某些狀況下是可以這麼做的, 例如上面的 flowtype 練習題。但這並不全然是對的,因為它其實只描述了 subtyping 的關係,而不是 immutability。 ## 小結 在查找資料後才發現原來到今天為止,號稱 strong type 並有支援 generic 的語言有部分並沒有完善支援,例如 TypeScript 的作法基本上就不檢查,直接讓你過(說好的 strict 呢?,而 Swift 則沒有支援 variance 語法,奇怪的是 Objective C 有。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -170,7 +170,7 @@ const d: { [string]: number } = c; ## 小結 在查找資料後才發現原來到今天為止,號稱 strong type 並有支援 generic 的語言有部分並沒有完善支援,例如 TypeScript 的作法基本上就不檢查,直接讓你過(說好的 strict 呢?,而 Swift 則沒有支援 variance 語法,奇怪的是 Objective C 有。 如果不用繼承,就能減少 subtyping 造成的 variance 問題。但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 30 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,7 +7,7 @@ - Mobile: Swift, Kotlin - Frontend: Flowtype, TypeScript - 後端語言:Python (pyre), Scala, [C#](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces), Java (沒錯!現在連 Java 都有) Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 strong type 基本上是少不了的。 例如 Array/List 如果沒有 generic 的話,只能有兩種做法: @@ -106,7 +106,7 @@ Java 無法在宣告 class 時宣告是否為 covariant,只能在宣告變數 ConstList<? extends Animal> list = new ConstList<Cat>(); ``` 以上就是所謂的 Covariance:「假設 A 是 B 的 subtype,那麼 `ConstList<A>` 就是 `ConstList<B>` 的 subtype」 那麼有沒有反過來「假設 A 是 B 的 subtype,那麼 `ConstList<B>` 就是 `ConstList<A>` 的 subtype」的呢? 有!它叫做 Contravariance。 @@ -147,4 +147,32 @@ Java Comparator<? super Cat> list = new Comparator<Animal>(); ``` ## Exercise 如果熟悉 FlowType 的話,這邊有一道題: 為什麼以下 FlowType 程式不能 typechecked?如何修改呢? ``` // FlowType const a: 1 = 1 const b: number = a const c: { [string]: 1 } = { 'a': 1 } const d: { [string]: number } = c; ``` ### Subtype Function? 其實沒有 generic 也有可能碰到 variance 問題。例如: 請問 function `Cat => void` 是不是 function `Animal => void` 的 subtype? ## 小結 在查找資料後才發現原來到今天為止,號稱 strong type 並有支援 generic 的語言有部分並沒有完善支援,例如 TypeScript, Swift 如果不用繼承,就能減少 subtyping 造成的 variance 問題。但是是不是沒有 subtyping 就完全沒有 variance 的問題呢? 答案是否定的,有興趣的朋友可以了解一下 Functor 跟 Contravariant Functor。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 38 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,9 +3,11 @@ 在近代高等程式語言快(ㄏㄨˋ)速(ㄒ一ㄤ)演(ㄔㄠ)化(ㄒㄧˊ)的狀況下,很多 strong type 的程式語言都開始有了 generic 的設計, 舉例來說: (這只是大略分類) - Mobile: Swift, Kotlin - Frontend: Flowtype, TypeScript - 後端語言:Python (pyre), Scala, C#, Java (沒錯!現在連 Java 都有) Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 strong type 基本上是少不了的。 例如 Array/List 如果沒有 generic 的話,只能有兩種做法: @@ -101,17 +103,48 @@ Java 無法在宣告 class 時宣告是否為 covariant,只能在宣告變數 也就是在撰寫的時候要特別小心。某種程度上也是算是 type system 設計上的缺失。 ```java ConstList<? extends Animal> list = new ConstList<Cat>(); ``` 以上就是所謂的 Covariance:「假設 A 是 B 的 subtype,那麼 ConstList<A> 就是 ConstList<B> 的 subtype」 那麼有沒有反過來「假設 A 是 B 的 subtype,那麼 `ConstList<B>` 就是 `ConstList<A>` 的 subtype」的呢? 有!它叫做 Contravariance。 ## 什麼情況下會用到 Contravariance? 當 generic type variable 只出現在 function input 的時候。 例如 `Comparator`,內涵一個 function 可以比較兩個東西的大小, 如果前者比較大則輸出大於 0 的數值,如果一樣就輸出 0,比較小則輸出小於零的數值。 `Comparator<Animal>` 可以比較 Animal 的大小,而 `Comparator<Cat>` 可以比較 Cat 的大小。 然後我們就可以來問一樣的問題:`Comparator<Animal>` 是不是 `Comparator<Cat>` 的 subtype? 要來回答個問題,我們要來重新理解一下什麼是 subtype:如果 A 是 B 的 subtype,那 B 能做到的事情 A 一定都能做到。 `Comparator<Animal>` 可以比較 `Animal`,而因為 Cat 是 Animal ,所以 `Comparator<Animal>` 當然也可以比較 Cat。 所以 `Comparator<Animal>` 是 `Comparator<Cat>` 的 subtype。 與 Covariance 相對應,他們分別的寫法是 FlowType ```flow class Comparator<-T> { ... } ``` Kotlin ```kotlin class Comparator<in T> { ... } ``` Java 一樣,Java 無法在宣告 class 時宣告是否為 contravariant,只能在宣告變數時使用。 ```java Comparator<? super Cat> list = new Comparator<Animal>(); ``` -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -83,12 +83,12 @@ ConstList<Cat> list = new ConstList<Cat>(cat1, cat2, cat3); FlowType ```flow class ConstList<+T> { ... } ``` (其實我本來想要用 TypeScript 說明的,結果一查資料才發現 TypeScript 在這塊實作其實是有問題的 [issue 1394](https://github.com/Microsoft/TypeScript/issues/1394)) Kotlin ```kotlin -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 4 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -104,7 +104,10 @@ Java 無法在宣告 class 時宣告是否為 covariant,只能在宣告變數 ConstList<? extends Animal> list = new ConstList<Cat>() ``` 以上就是所謂的 Covariance:「假設 A 是 B 的 subtype,那麼 ConstList<A> 就是 ConstList<B> 的 subtype」 那麼有沒有反過來「假設 A 是 B 的 subtype,那麼 ConstList<B> 就是 ConstList<A> 的 subtype」的呢? 有!它叫做 Contravariance。 ## 什麼情況下會用到 Contravariance? -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -65,7 +65,7 @@ Cat cat = listOfCat.get(0) // 取得 Animal 那假設我們想做一個特殊的 `List` 使得 `List<Cat>` 是 `List<Animal>` 的 subtype 呢? ## 什麼是 Covariance? 首先,在上述的例子中我們已經知道如果我們可以「塞」東西進去這個 List , 那就不可能做到「`List<Cat>` 是 `List<Animal>` 的 subtype」。 @@ -104,6 +104,8 @@ Java 無法在宣告 class 時宣告是否為 covariant,只能在宣告變數 ConstList<? extends Animal> list = new ConstList<Cat>() ``` 以上就是所謂的 Covariance。 ## 什麼情況下會用到 Contravariance? (待續 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -65,7 +65,7 @@ Cat cat = listOfCat.get(0) // 取得 Animal 那假設我們想做一個特殊的 `List` 使得 `List<Cat>` 是 `List<Animal>` 的 subtype 呢? ## 如何宣告 covariant generic type 首先,在上述的例子中我們已經知道如果我們可以「塞」東西進去這個 List , 那就不可能做到「`List<Cat>` 是 `List<Animal>` 的 subtype」。 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 4 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -98,11 +98,15 @@ class ConstList<out T> { ... } Java Java 無法在宣告 class 時宣告是否為 covariant,只能在宣告變數時使用。 也就是在撰寫的時候要特別小心。某種程度上也是算是 type system 設計上的缺失。 ```java ConstList<? extends Animal> list = new ConstList<Cat>() ``` ## 什麼情況下會用到 Contravariance? (待續 -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 12 additions and 12 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,10 +23,10 @@ Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 「碰到就想辦法繞過」這種解決方式。而個人在工作上也有碰到因為對 generic 一知半解而設計出來得詭異架構。要了解什麼是 covariance ,我們必須先了解 generic 碰上 subtype 會出現什麼樣的問題,其中最經典的: ## `List<Cat>` 是不是 `List<Animal>` 的 subtype? 乍看之下答案是「當然是啊,`Cat` 是 `Animal`,當然 `List<Cat>` 就是 `List<Animal>` 啊」,但實際上並沒有這麼簡單。 我們先假設 `List<Cat>` 是 `List<Animal>` 這件事情成立好了,這就代表: ```java List<Cat> listOfCat = // 總之,某個方法生出來的,不是重點所以略過 @@ -40,13 +40,13 @@ List<Animal> listOfAnimal = listOfCat; listOfAnimal.add(new Dog()); ``` 光看這一行可能沒問題,但同時考慮前段 code 就能看出錯誤:`listOfAnimal` 實際上是 `listOfCat`,而其 type 是 `List<Cat>`。 也就是,你正在把 dog 塞進一個只能放 Cat 的 List,而如果其他地方還持有 `listOfCat` 的 reference 的話,就有可能拿到 dog, 而 dog 不是 cat,當然就是個 type error。 既然如此,那反過來說 ## `List<Cat>` 能不能是 `List<Animal>` 的 supertype? 答案是....看情況。 @@ -58,17 +58,17 @@ listOfAnimal.add(new Dog()); Cat cat = listOfCat.get(0) // 取得 Animal ``` 因為 `listOfCat` 如果是 `listOfAnimal` 的 supertype 的話,代表 `listOfCat` 有可能實際上是 `listOfAnimal`, 也就是你取到的值有可能是 `Animal` 而不是 `Cat`,而 `Animal` 並不是 `Cat` 的 subtype,於是這邊會產生另外一個 type error。 所以以常識上的通用 `List` 來說的話,`List<Cat>` 既不是 `List<Animal>` 的 subtype 也不是 supertype。 那假設我們想做一個特殊的 `List` 使得 `List<Cat>` 是 `List<Animal>` 的 subtype 呢? ## Covariance 首先,在上述的例子中我們已經知道如果我們可以「塞」東西進去這個 List , 那就不可能做到「`List<Cat>` 是 `List<Animal>` 的 subtype」。 所以我們要另外定一個 list 叫做 ConstList,意思是這個 list 是 constant,也就是我們不能改動裡面的值。 你可能會問:「既然這個 list 不能塞東西進去,那我要這個 list 幹麻?」,雖然不能塞東西進去, 但是你可以在產生這個 list 的時候就把值設定好,例如 @@ -77,7 +77,7 @@ Cat cat = listOfCat.get(0) // 取得 Animal ConstList<Cat> list = new ConstList<Cat>(cat1, cat2, cat3); ``` 那麼,我們要怎麼告訴 typechecker 說「假設 A 是 B 的 subtype,那麼 `ConstList<A>` 就是 `ConstList<B>` 的 subtype」呢? 以下舉幾個程式語言當例子: -
rayshih revised this gist
Jun 8, 2020 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -25,7 +25,7 @@ Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 ## List<Cat> 是不是 List<Animal> 的 subtype? 乍看之下答案是「當然是啊,Cat 是 Animal,當然 `List<Cat>` 就是 List<Animal> 啊」,但實際上並沒有這麼簡單。 我們先假設 List<Cat> 是 List<Animal> 這件事情成立好了,這就代表: ```java -
rayshih revised this gist
Jun 7, 2020 . 1 changed file with 51 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,5 @@ # 你可能其實不懂繼承 - 什麼是 Covariance 跟 Contravariance 在近代高等程式語言快(ㄏㄨˋ)速(ㄒ一ㄤ)演(ㄔㄠ)化(ㄒㄧˊ)的狀況下,很多 strong type 的程式語言都開始有了 generic 的設計, 舉例來說: @@ -8,7 +10,7 @@ Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 strong type 基本上是少不了的。 例如 Array/List 如果沒有 generic 的話,只能有兩種做法: 1. 把所有東西都轉成 Object or Any type 才能塞進 List,然後在從 List 取出物件的時候再做 down casting。 2. 針對需要的 object type 個別實作相對應的 List type,例如:Cat 就有 CatList。 第一個方法的問題是:你有可能 down cast 到錯的 type,譬如說你本來塞的是 Cat,但你拿出來後 cast 成 Dog。 @@ -21,7 +23,7 @@ Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 「碰到就想辦法繞過」這種解決方式。而個人在工作上也有碰到因為對 generic 一知半解而設計出來得詭異架構。要了解什麼是 covariance ,我們必須先了解 generic 碰上 subtype 會出現什麼樣的問題,其中最經典的: ## List<Cat> 是不是 List<Animal> 的 subtype? 乍看之下答案是「當然是啊,Cat 是 Animal,當然 List<Cat> 就是 List<Animal> 啊」,但實際上並沒有這麼簡單。 我們先假設 List<Cat> 是 List<Animal> 這件事情成立好了,這就代表: @@ -35,29 +37,72 @@ List<Animal> listOfAnimal = listOfCat; ```java listOfAnimal.add(new Dog()); ``` 光看這一行可能沒問題,但同時考慮前段 code 就能看出錯誤:listOfAnimal 實際上是 listOfCat,而其 type 是 List<Cat>。 也就是,你正在把 dog 塞進一個只能放 Cat 的 List,而如果其他地方還持有 listOfCat 的 reference 的話,就有可能拿到 dog, 而 dog 不是 cat,當然就是個 type error。 既然如此,那反過來說 ## List<Cat> 是不是 List<Animal> 的 supertype? 答案是....看情況。 那麼什麼情況下不行呢? 如果你今天試著從 list 拿值出來就不行。 ``` Cat cat = listOfCat.get(0) // 取得 Animal ``` 因為 listOfCat 如果是 listOfAnimal 的 supertype 的話,代表 listOfCat 有可能實際上是 listOfAnimal, 也就是你取到的值有可能是 Animal 而不是 Cat,而 Animal 並不是 Cat 的 subtype,於是這邊會產生另外一個 type error。 所以以常識上的通用 List 來說的話,List<Cat> 既不是 List<Animal> 的 subtype 也不是 supertype。 那假設我們想做一個特殊的 List 使得 List<Cat> 是 List<Animal> 的 subtype 呢? ## Covariance 首先,在上述的例子中我們已經知道如果我們可以「塞」東西進去這個 List , 那就不可能做到「List<Cat> 是 List<Animal> 的 subtype」。 所以我們要另外定一個 list 叫做 ConstList,意思是這個 list 是 constant,也就是我們不能改動裡面的值。 你可能會問:「既然這個 list 不能塞東西進去,那我要這個 list 幹麻?」,雖然不能塞東西進去, 但是你可以在產生這個 list 的時候就把值設定好,例如 ``` ConstList<Cat> list = new ConstList<Cat>(cat1, cat2, cat3); ``` 那麼,我們要怎麼告訴 typechecker 說「假設 A 是 B 的 subtype,那麼 ConstList<A> 就是 ConstList<B> 的 subtype」呢? 以下舉幾個程式語言當例子: FlowType (其實我本來想要用 TypeScript 說明的,結果一查資料才發現 TypeScript 在這塊實作其實是有問題的 [issue 1394](https://github.com/Microsoft/TypeScript/issues/1394)) ```flow class ConstList<+T> { ... } ``` Kotlin ```kotlin class ConstList<out T> { ... } ``` Java Java 無法在宣告 class 時宣告是否為 covariant,只能在宣告變數時使用。 ```java ConstList<? extends Animal> list = new ConstList<Cat>() ``` -
rayshih created this gist
Jun 7, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,63 @@ 在近代高等程式語言快(ㄏㄨˋ)速(ㄒ一ㄤ)演(ㄔㄠ)化(ㄒㄧˊ)的狀況下,很多 strong type 的程式語言都開始有了 generic 的設計, 舉例來說: - Mobile: Swift, Kotlinn - Frontend: Flowtype, TypeScript - 後端語言:Python (pyre), Scala, Java (沒錯!現在連 Java 都有) Generic type 雖然看起來是比較新的東西,但如果要做到嚴格的 strong type 基本上是少不了的。 例如 Array/List 如果沒有 generic 的話,只能有兩種做法: 1. 把所有東西都轉成 Object or Any type 才能塞進 Array,然後在從 List 取出物件的時候再做 down casting。 2. 針對需要的 object type 個別實作相對應的 List type,例如:Cat 就有 CatList。 第一個方法的問題是:你有可能 down cast 到錯的 type,譬如說你本來塞的是 Cat,但你拿出來後 cast 成 Dog。 這樣就會噴 Runtime Error。而第二種方法的確是 type safe ,但因為每個 type 都要重寫一堆邏輯,非常不方便。 現在我們知道 generic 的方便性及重要性了。接下來我們來進入本篇想討論的:Covariance 跟 Contravariance。 基本上, generic 的出現代表了 type system 增加了一個新的維度,碰上有 subtype 的 type system 會變得比平常複雜。 偏偏現代幾乎所有程式語言都有 inheritance,所以這變成了現在 programmer 早晚會碰到的問題。我聽到的 case 都幾乎是走 「碰到就想辦法繞過」這種解決方式。而個人在工作上也有碰到因為對 generic 一知半解而設計出來得詭異架構。要了解什麼是 covariance ,我們必須先了解 generic 碰上 subtype 會出現什麼樣的問題,其中最經典的: > List<Cat> 是不是 List<Animal> 的 subtype? 乍看之下答案是「當然是啊,Cat 是 Animal,當然 List<Cat> 就是 List<Animal> 啊」,但實際上並沒有這麼簡單。 我們先假設 List<Cat> 是 List<Animal> 這件事情成立好了,這就代表: ```java List<Cat> listOfCat = // 總之,某個方法生出來的,不是重點所以略過 List<Animal> listOfAnimal = listOfCat; ``` 以上,根據假設沒問題,但接下來的 code 就不合理了: ```java listOfAnimal.put(new Dog()); ``` 光看這一行可能沒問題,但同時考慮前段 code 就能看出錯誤:listOfAnimal 實際上是 listOfCat,而其 type 是 List<Cat>。 也就是,你正在把 dog 塞進一個只能放 Cat 的 List,而如果其他地方還持有 listOfCat 的 reference 的話,就有可能拿到 dog, 而 dog 不是 cat,當然就是個 type error。 既然如此,那反過來說我們能不能把 List<Cat> 當作是 List<Animal> 的 supertype 呢?答案是....看情況。 那麼什麼情況下不行呢? 如果你今天試著從 list 拿值出來就不行。 ``` Cat cat = listOfCat.get(0) ``` 因為 listOfCat 如果是 listOfAnimal 的 supertype 的話,代表 listOfCat 有可能實際上是 listOfAnimal, 也就是你取到的值有可能是 Animal 而不是 Cat,而 Animal 並不是 Cat 的 subtype,於是這邊會產生另外一個 type error。 所以以常識上的通用 List 來說的話,List<Cat> 既不是 List<Animal> 的 subtype 也不是 supertype。 (待續