2011-11-25 82 views
7

以下是可能用Scala:Scala的構造抽象

scala> val l = List 
l: scala.collection.immutable.List.type = [email protected] 

scala> l (1, 2, 3) 
res0: List[Int] = List(1, 2, 3) 

換句話說,Scala有高階的多態性。我想用高階多態來完成以下工作。

sealed abstract class A { def eval() : A } 
case class A0() extends A { ... } 
case class A1 (a : A) extends A { ... } 
case class A2 (a : A, b : A) extends A { ... } 
.... 

所以我有一堆case類的A,其子類的構造函數不一定採取的參數相同的數字。我也想有一個「通用」案例類,像這樣:

case class ApplyA (c : ???, l : List [ A ]) extends A { 
    def eval() : A = { ??? } } 

的想法是,ApplyA需要作爲第一個參數的構造函數的東西,這是A一個亞型,和參數列表。然後eval方法在可能的情況下用構造函數構造適當的類(即列表具有正確的長度)並返回 (這對應於上述List示例中的l (1, 2, 3))。 ApplyA的第一個構造函數的參數類型是什麼?

這應該是可能的更高階多態性,但我不知道如何。我知道我可以做到這一點,即使不使用更高階的多態性,只需將構造函數包裝在函數中,然後將這些函數作爲第一個參數傳遞給ApplyA的構造函數,但我想了解如何直接使用更高階多態。

回答

9

問題是List示例根本不涉及任何更高階的多態性。 List.apply只是採用可變數目的參數:

def apply(xs: A*) 

高階多態性涉及方法或類型,這取類型構造爲類型參數,例如

def fmap[F[_], A](x: F[A]): F[B] 

所以不行,你不能使用高階多態。

11

@alexey_r是非常正確的,您的List示例不涉及高階多態性。但是如果你準備使用一些type-level heavy artillery,你可以抽象出構造函數的構造函數,以獲得與你所要求的東西非常接近的東西。

第一點要注意的是,正如所寫的,你的「通用」類不可能實現,

case class ApplyA(c : ???, l : List[A]) ... 

因爲這裏的構造c的元數和之間沒有編譯時可檢查的關係名單的長度爲l。我們可以通過一個HList有一個HList參數更換List和幫助自己從任意元數的普通功能的轉換功能,解決這個問題,

import shapeless.HList._ 
import shapeless.Functions._ 

sealed abstract class A { def eval() : A } 
case class A0() extends A { def eval() = this } 
case class A1 (a : A) extends A { def eval() = this } 
case class A2 (a : A, b : A) extends A { def eval() = this } 

case class ApplyA[C, L <: HList, HF](c : C, l : L) 
    (implicit hl : FnHListerAux[C, HF], ev : HF <:< (L => A)) extends A { 
    def eval() : A = hl(c)(l) 
    } 

val a : A = A0() 

val a0 = ApplyA(A0.apply _, HNil) 
val a1 = ApplyA(A1.apply _, a :: HNil) 
val a2 = ApplyA(A2.apply _, a :: a :: HNil) 

的隱含參數hl : FnHListerAux[C, HF]從你的構造提供了一個轉換,不管它是什麼樣的,都可以從一個單一的HList論證中得到一個函數。隱含的參數ev : HF <:< (L => A)證明構造函數參數提供的HList的長度是正確的長度(和類型FWIW,但在這個例子中幾乎沒有關係)。