<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                上一章介紹了類型類的概念,這種模式使設計出來的程序既擁抱擴展性,又不放棄具體的類型信息。這一章,我們還將繼續探究 Scala 的類型系統,講講另一個特性,這個特性可以將 Scala 與其他主流編程語言區分開:依賴類型,特別是,路徑依賴的類型和依賴方法類型。 一個廣泛用于反對靜態類型的論點是 “the compiler is just in the way”,最終得到的都是數據,為什么還要建立一個復雜的類型層次結構? 到最后,靜態類型的唯一目的就是,讓“超級智能”的編譯器來定期“羞辱”編程人員,以此來預防程序的 bug,在事情變得糟糕之前,保證你做出正確的選擇。 路徑依賴類型是一種強大的工具,它把只有在運行期才知道的邏輯放在了類型里,編譯器可以利用這一點減少甚至防止 bug 的引入。 有時候,意外的引入路徑依賴類型可能會導致難堪的局面,尤其是當你從來沒有聽說過它。因此,了解和熟悉它絕對是個好主意,不管以后要不要用。 ### 問題 先從一個問題開始,這個問題可以由路徑依賴類型幫我們解決:在同人小說中,經常會發生一些駭人聽聞的事情。比如說,兩個主角去約會,即使這樣的情景有多么的不合常理,甚至還有穿越的同人小說,兩個來自不同系列的角色互相約會。 不過,好的同人小說寫手對此是不屑一顧的。肯定有什么模式來阻止這樣的錯誤做法。下面是這種領域模型的初版: ~~~ object Franchise { case class Character(name: String) } class Franchise(name: String) { import Franchise.Character def createFanFiction( lovestruck: Character, objectOfDesire: Character): (Character, Character) = (lovestruck, objectOfDesire) } ~~~ 角色用 `Character` 樣例類表示, `Franchise` 類有一個方法,這個方法用來創建有關兩個角色的小說。下面代碼創建了兩個系列和一些角色: ~~~ val starTrek = new Franchise("Star Trek") val starWars = new Franchise("Star Wars") val quark = Franchise.Character("Quark") val jadzia = Franchise.Character("Jadzia Dax") val luke = Franchise.Character("Luke Skywalker") val yoda = Franchise.Character("Yoda") ~~~ 不幸的是,這一刻,我們無法阻止不好的事情發生: ~~~ starTrek.createFanFiction(lovestruck = jadzia, objectOfDesire = luke) ~~~ 多么恐怖的事情!某個人創建了一段同人小說,婕琪戴克斯和天行者盧克竟然在約會!我們不應該容忍這樣的事情。 > 婕琪戴克斯:星際迷航中的角色:[http://en.wikipedia.org/wiki/Jadzia_Dax](http://en.wikipedia.org/wiki/Jadzia_Dax)天行者盧克:星球大戰中的角色:[http://en.wikipedia.org/wiki/Luke_Skywalker](http://en.wikipedia.org/wiki/Luke_Skywalker) 你的第一直覺可能是,在運行期做一些檢查,保證約會的兩個角色來自同一個特許商。比如說: ~~~ object Franchise { case class Character(name: String, franchise: Franchise) } class Franchise(name: String) { import Franchise.Character def createFanFiction( lovestruck: Character, objectOfDesire: Character): (Character, Character) = { require(lovestruck.franchise == objectOfDesire.franchise) (lovestruck, objectOfDesire) } } ~~~ 現在,每個角色都有一個指向所屬發行商的引用,試圖創建包含不同系列角色的小說會引發 `IllegalArgumentException` 異常。 ### 路徑依賴類型 這挺好,不是嗎?畢竟這是被灌輸多年的行為方式:快速失敗。然而,有了 Scala,我們能做的更好。有一種可以更快速失敗的方法,不是在運行期,而是在編譯期。為了實現它,我們需要將 `Character` 和它的 `Franchise` 之間的聯系編碼在類型層面上。 Scala **嵌套類型** 工作的方式允許我們這樣做。一個嵌套類型被綁定在一個外層類型的實例上,而不是外層類型本身。這意味著,如果將內部類型的一個實例用在包含它的外部類型實例外面,會出現編譯錯誤: ~~~ class A { class B var b: Option[B] = None } val a1 = new A val a2 = new A val b1 = new a1.B val b2 = new a2.B a1.b = Some(b1) a2.b = Some(b1) // does not compile ~~~ 不能簡單的將綁定在 `a2` 上的類型 `B` 的實例賦值給 `a1` 上的字段:前者的類型是 `a2.B` ,后者的類型是 `a1.B` 。中間的點語法代表類型的路徑,這個路徑通往其他類型的具體實例。因此命名為路徑依賴類型。 下面的代碼運用了這一技術: ~~~ class Franchise(name: String) { case class Character(name: String) def createFanFictionWith( lovestruck: Character, objectOfDesire: Character): (Character, Character) = (lovestruck, objectOfDesire) } ~~~ 這樣,類型 `Character` 嵌套在 `Franchise` 里,它依賴于一個特定的 `Franchise` 實例。 重新創建幾個角色和發行商: ~~~ val starTrek = new Franchise("Star Trek") val starWars = new Franchise("Star Wars") val quark = starTrek.Character("Quark") val jadzia = starTrek.Character("Jadzia Dax") val luke = starWars.Character("Luke Skywalker") val yoda = starWars.Character("Yoda") ~~~ 把角色放在一起構成小說: ~~~ starTrek.createFanFictionWith(lovestruck = quark, objectOfDesire = jadzia) starWars.createFanFictionWith(lovestruck = luke, objectOfDesire = yoda) ~~~ 順利編譯!接下來,試著去把 `jadzia` 和 `luke` 放在一起: ~~~ starTrek.createFanFictionWith(lovestruck = jadzia, objectOfDesire = luke) ~~~ 不應該的事情就會編譯失敗!編譯器抱怨類型不匹配: ~~~ found : starWars.Character required: starTrek.Character starTrek.createFanFictionWith(lovestruck = jadzia, objectOfDesire = luke) ~~~ 即使這個方法不是在 `Franchise` 中定義的,這項技術同樣可用。這種情況下,可以使用依賴方法類型,一個參數的類型信息依賴于前面的參數。 ~~~ def createFanFiction(f: Franchise)(lovestruck: f.Character, objectOfDesire: f.Character) = (lovestruck, objectOfDesire) ~~~ 可以看到, `lovestruck` 和 `objectOfDesire` 參數的類型依賴于傳遞給該方法的 `Franchise` 實例。不過請注意:被依賴的實例只能在一個單獨的參數列表里。 ### 抽象類型成員 依賴方法類型通常和抽象類型成員一起使用。假設我們在開發一個鍵值存儲,只支持讀取和存放操作,但是類型安全的。下面是一個簡化的實現: ~~~ object AwesomeDB { abstract class Key(name: String) { type Value } } import AwesomeDB.Key class AwesomeDB { import collection.mutable.Map val data = Map.empty[Key, Any] def get(key: Key): Option[key.Value] = data.get(key).asInstanceOf[Option[key.Value]] def set(key: Key)(value: key.Value): Unit = data.update(key, value) } ~~~ 我們定義了一個含有抽象類型成員 `Value` 的類 `Key`。`AwesomeDB` 中的方法可以引用這個抽象類型,即使不知道也不關心它到底是個什么表現形式。 定義一些想使用的具體的鍵: ~~~ trait IntValued extends Key { type Value = Int } trait StringValued extends Key { type Value = String } object Keys { val foo = new Key("foo") with IntValued val bar = new Key("bar") with StringValued } ~~~ 之后,就可以存放鍵值對了: ~~~ val dataStore = new AwesomeDB dataStore.set(Keys.foo)(23) val i: Option[Int] = dataStore.get(Keys.foo) dataStore.set(Keys.foo)("23") // does not compile ~~~ ### 實踐中的路徑依賴類型 在典型的 Scala 代碼中,路徑依賴類型并不是那么無處不在,但它確實是有很大的實踐價值的,除了給同人小說建模之外。 最普遍的用法是和 **cake pattern** 一起使用,cake pattern 是一種組件組合和依賴管理的技術。冠以這一點,可以參考 Debasish Ghosh 的 [文章](http://debasishg.blogspot.ie/2013/02/modular-abstractions-in-scala-with.html) 。 把一些只有在運行期才知道的信息編碼到類型里,比如說:異構列表、自然數的類型級別表示,以及在類型中攜帶大小的集合,路徑依賴類型和依賴方法類型有著至關重要的角色。Miles Sabin 正在 [Shapeless](https://github.com/milessabin/shapeless) 中探索 Scala 類型系統的極限。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看