2017-06-14 38 views
1

最初由3個類型類型設計的軟件已經發展到使用大量的類型類。讓我們以這個例子:存在越來越多的類型分類時的分層軟件

  • 你有Service[A,B]
  • 你有TypeClass1[A]
  • 你有TypeClass2[B]
  • 你有TypeClass3[A,B,C]
  • 你有一個TypeClass4[A]
  • 你有TypeClass5[B]
  • 您有TypeClass6[C]
  • 你有TypeClass7[C]
  • 你有TypeClass8[B]

我們有這是完全不相交的A,B,C的6個已知組合(即每種組合都包含僅在該組合中使用的A,B,C類型)。所以,現在我們有兩個選擇:

  1. 寫這將需要8種不同類型類
  2. 創建中間類型類,我們可以推導出如 TypeClass14[A]Typeclass67[C]等頂級方法,所以最後你 能最終得到一個UberTypeClass[A,B,C],它提供了所有的 所需的行爲。

作爲一種通用的編程方法,第二種解決方案顯然更合理:引入中間層和抽象提高了代碼質量的多種方式。我們面臨的挑戰是:

  1. 如果我們沒有在中間層通過提供堆疊起來的隱式轉換,在現實中我們只是提高了語法召喚較低級別的類型類(但不管怎麼說,這些都是implicits!),但如果我們不召喚下級類型類我們沒有解決的代碼缺乏層次感

  2. 問題,我們最終寫了很多中間樣板(TypeClass1InstancesTypeClass2Instances的,Typeclass3Instances

當使用與軟件分層相關的類型類時,什麼是最佳實踐?

回答

1

你可以使用類似下面的東西來捆綁類型類。

你也可以使用無形狀的HList將這個代碼推廣到任意的形式。

trait Lemma2[F1[_], F2[_], A] { 
    val F1: F1[A] 
    val F2: F2[A] 
} 

object Lemma2 { 
    implicit def lemma[A, F1[_], F2[_]](implicit FA: F1[A], FB: F2[A]): 
    Lemma2[F1, F2, A] = 
    new Lemma2[F1, F2, A] { 
     val F1 = FA 
     val F2 = FB 
    } 

} 

trait LemmaSyntax { 
    implicit def f1[F1[_], F2[_], A](implicit lemma: Lemma2[F1, F2, A]): F1[A] = { 
    lemma.F1 
    } 

    implicit def f2[F1[_], F2[_], A](implicit lemma: Lemma2[F1, F2, A]): F2[A] = { 
    lemma.F2 
    } 
} 

import cats._ 
import cats.implicits._ 

object Test extends LemmaSyntax { 
    type Proof[A] = Lemma2[Show, Eq, A] 


    def foo[A : Proof](a: A): String = { 
    a.show 
    } 
} 

object Other { 
    Test.foo("moo") 
} 
+0

這樣做的方向是將所有類型暴露給頂層而不構建中間抽象。這不違反模塊化嗎? – Edmondo1984

+0

這取決於你如何看待它。讓我們考慮一下我有一個需要'TC1 [_]'和'TC2 [_]'的函數。在沒有這種複合類型類服從的規律的情況下,我真的只想對類型系統說,這個函數需要這兩個類型類遵循類型類的自身定律。這經常發生在應用程序代碼中。在另一種情況下,如果有法律要遵守,單純的存在是不夠的,所以我會引入另一個'TC'。 – yw3410

+1

後者的一個很好的例子是一個'編碼器[A]'和一個'解碼器[A]'。把它們放在一起,你會得到一個需要遵循法律的'Codec [A]'a.encode.decode === a'。另一方面,我不能在示例中的'Show'和'Eq'之間陳述任何新的東西。 – yw3410