2011-11-25 40 views
7

Sau đây là có thể trong Scala:Scala constructor trừu tượng

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

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

Nói cách khác, Scala có bậc cao đa hình. Tôi muốn sử dụng đa hình bậc cao hơn để làm như sau.

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 { ... } 
.... 

Vì vậy, tôi có một loạt các trường hợp, lớp con của A, mà nhà thầu không nhất thiết phải lấy cùng số đối số. Tôi cũng muốn có một lớp trường hợp 'chung', một cái gì đó như thế này:

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

Ý tưởng là ApplyA mất như là đối số đầu tiên một constructor kiếm cái gì đó là một subtype của A, và một danh sách các đối số. Phương pháp eval sau đó xây dựng một lớp thích hợp với hàm tạo nếu có thể (nghĩa là danh sách có độ dài phù hợp) và trả về (điều này tương ứng với l (1, 2, 3) trong ví dụ List ở trên). Điều gì sẽ là loại đối số của hàm tạo đầu tiên cho ApplyA?

Điều này có thể xảy ra với đa hình bậc cao hơn, nhưng tôi không thể tìm ra cách thực hiện. Tôi biết rằng tôi có thể làm điều này ngay cả khi không sử dụng đa hình bậc cao bằng cách đơn giản bao bọc các hàm khởi tạo trong các hàm và sau đó truyền các hàm này làm đối số đầu tiên cho hàm dựng cho ApplyA, nhưng tôi muốn hiểu cách sử dụng đa hình bậc cao trực tiếp.

Trả lời

9

Vấn đề là ví dụ List không liên quan đến bất kỳ đa hình bậc cao nào cả. List.apply chỉ mất một số biến của tham số:

def apply(xs: A*) 

cao hơn bậc đa hình liên quan đến phương pháp, hoặc các loại, trong đó có nhà xây dựng kiểu như tham số kiểu, ví dụ

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

Vì vậy, không, bạn không thể làm điều đó bằng cách sử dụng đa hình bậc cao hơn.

11

@alexey_r hoàn toàn đúng với ví dụ List của bạn không liên quan đến tính đa hình bậc cao. Nhưng nếu bạn đã sẵn sàng để sử dụng một số type-level heavy artillery bạn có thể tóm tắt về sự tinh hoa của các nhà xây dựng A{0,1,2} của bạn để có được một cái gì đó trông khá gần với những gì bạn đang yêu cầu.

Điểm đầu tiên cần lưu ý là, khi viết, lớp 'chung' của bạn có thể không có thể được thực hiện,

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

vì không có thời gian biên dịch mối quan hệ checkable giữa arity của các nhà xây dựng c và độ dài của danh sách l. Chúng ta có thể khắc phục vấn đề đó bằng cách thay thế List với một HList và giúp đỡ chính mình để chuyển đổi từ chức năng bình thường với arity tùy ý để chức năng với một HList đối số duy nhất,

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) 

Đối số ngầm hl : FnHListerAux[C, HF] cung cấp một sự chuyển đổi từ constructor của bạn, bất cứ điều gì đó là sự thánh thiện, với một hàm từ một đối số HList duy nhất.Và đối số ngầm định ev : HF <:< (L => A) chứng kiến ​​rằng độ dài của các đối số hàm dựng là HList được cung cấp có độ dài chính xác (và loại FWIW, nhưng điều đó hầu như không có liên quan trong ví dụ này).

Các vấn đề liên quan