2013-06-27 28 views
6

Tôi đang làm việc trên một thư viện nhỏ cho các mô hình kinh tế kiểm tra các đơn vị của các thực thể, sử dụng các loại, ví dụ: thay vì val apples = 2.0, chúng tôi viết val apples = GoodsAmount[KG, Apples](2.0). Để tạo bó hàng hóa, tôi đang cố gắng sử dụng HLists từ thư viện không có hình dạng. Điều này làm việc tốt, nhưng trong một số trường hợp tôi không thể được như mã chung như tôi thích. Xem ví dụ vấn đề sau.Shapeless: Kiểm tra loại ràng buộc của các chức năng đa hình

Tôi bắt đầu bằng một mã đơn giản giải thích những gì tôi muốn chuyển thành không có hình dạng. Chúng tôi tạo ra hai lớp, đại diện cho Km, các Miles khác. Nó nên được phép thêm các lớp Km, nhưng không phải dặm. Rằng tôi sử dụng một kiểu trừu tượng T chủ yếu là động lực làm thư viện phức tạp hơn của chúng ta. Và cuộc gọi gián tiếp đến chức năng '+' chỉ là vì chúng ta cần một cái gì đó tương tự trong trường hợp không có hình dạng phía sau.

trait Foo { 
    type T 
    val v: Double 
    def +[B <: Foo](other: B)(implicit ev: this.T =:= other.T) = v + other.v 
} 

trait _Km 
trait _Miles 

case class Km(v: Double) extends Foo { type T = _Km } 
case class Miles(v: Double) extends Foo { type T = _Miles } 

object ExampleSimple extends App { 
    def add[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: a.T =:= b.T) = { a + b } 

    add(Km(1), Km(2)) 
    // add(Km(1), Miles(2)) /* does not compile as intended */ 
} 

Điều này hoạt động như dự định. Nhưng cần phải kiểm tra Loại Contraint trên chức năng 'thêm'. nỗ lực của tôi để mở rộng này để HLists trông như thế này:

object ExampleShapeless extends App { 
    import shapeless._ 

    val l1 = Km(1) :: Km(2) :: HNil 
    val l2 = Km(4) :: Km(3) :: HNil 

    object add extends Poly1 { 
    implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a, b) => a + b } 
    } 

    (l1 zip l2).map(add) 
} 

Nhưng điều này tạo ra các thông báo sau lỗi (sử dụng Scala 2.10.2):

[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:50: Cannot prove that a.T =:= b.T. 
[error]  implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a: Foo, b) => a + b } 
[error]                  ^
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:54: could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]] 
[error] (l1 zip l2).map(add) 

Lỗi đầu tiên cần được cố định, trong trường hợp rằng tôi có thể thêm một ràng buộc kiểu vào hàm caseTuple, nhưng thành thật mà nói, tôi đã không hiểu cách hàm tại đang làm việc và nơi tôi có thể thêm tham số bằng chứng ngầm. Và tôi cũng không biết, những gì tôi phải làm, để Mapper sẽ tìm thấy giá trị tiềm ẩn của anh ta.

Một phiên bản ít generic, nơi tôi replase chức năng caseTuple với

implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b } 

hoạt động tốt, nhưng sẽ cần phải viết rất nhiều mã dư thừa (okay, giải pháp này sẽ vẫn tốt hơn là giải pháp hiện tại của chúng tôi sử dụng Tuples). Ai đó có thể cho tôi một gợi ý làm thế nào tôi có thể giải quyết vấn đề này?

Cảm ơn, Klinke

+0

Bạn có thể thử định nghĩa 'Foo' của bạn như sau:' trait Foo [T <: Foo] {v: Double; + (t T): T = ...} '. 'class Km (val v: Double) mở rộng Foo [Km]'. 'ngầm định def thêm [T] = tại [(Foo [T], Foo [T])]' – senia

Trả lời

7

Bạn có thể yêu cầu các thành viên loại để phù hợp bằng cách thêm một tham số kiểu để các trường hợp:

object add extends Poly1 { 
    implicit def caseTuple[_T, A <: Foo { type T = _T }] = at[(A, A)] { 
    case (a, b) => a + b 
    } 
} 

Hoặc bạn có thể sử dụng một loại tồn tại, vì bạn chỉ thực sự quan tâm đó chúng giống nhau:

object add extends Poly1 { 
    implicit def caseTuple[A <: Foo { type T = _T } forSome { type _T }] = 
    at[(A, A)] { 
     case (a, b) => a + b 
    } 
} 

Hoặc là phiên bản sẽ cung cấp hành vi bạn muốn.

+0

Cảm ơn, điều này hoạt động tốt cũng trường hợp phức tạp hơn của tôi ;-) Nhưng tôi vẫn còn vấn đề với giá trị tiềm ẩn bị thiếu cho Người lập bản đồ. Tôi sẽ cố tự giải quyết nó, nhưng có lẽ bạn cũng có thể giúp tôi ở đây? – Klinke

+0

OK, đã tìm thấy giải pháp cho phiên bản đơn giản, thêm ngữ cảnh được ràng buộc cho A đến để trợ giúp. Vì vậy, bây giờ tôi có 'implicit def caseTuple [_t, A <: Foo {type T = _T} <% Foo {type T = _T}] = ...' Lần này giải pháp không dịch dễ dàng đến phiên bản đầy đủ của tôi , nhưng hy vọng tôi cũng có thể khắc phục các vấn đề mới. – Klinke

+0

@Klinke: Tôi không chắc mình có hiểu vấn đề không - nếu tôi sao chép và dán 'add' vào phần tử' ExampleShapeless' của bạn thì có vẻ như hoạt động như mong đợi. –

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