2014-09-07 24 views
5

Tôi dự định lọc theo một HList theo kiểu biến đổi - tôi cũng muốn bao gồm các lớp con. Vì vậy, bộ lọc covariant trên Foo nên nắm bắt các thành phần của Foo cũng như Bar. Tôi đã xây dựng ví dụ này thử <:!<, để xem nếu nó làm những gì tôi muốn nó làm.Thực hiện một bộ lọc biến đổi trên một HList

http://scastie.org/6465

/*** 
scalaVersion := "2.11.2" 

libraryDependencies ++= Seq(
    "com.chuusai" %% "shapeless" % "2.0.0" 
) 
*/ 

import shapeless._ 

final class HListOps[L <: HList](l: L) { 
    trait CoFilter[L <: HList, U] extends DepFn1[L] { type Out <: HList } 

    object CoFilter { 
    def apply[L <: HList, U](implicit filter: CoFilter[L, U]): Aux[L, U, filter.Out] = filter 

    type Aux[L <: HList, U, Out0 <: HList] = CoFilter[L, U] { type Out = Out0 } 

    implicit def hlistCoFilterHNil[L <: HList, U]: Aux[HNil, U, HNil] = 
     new CoFilter[HNil, U] { 
     type Out = HNil 
     def apply(l: HNil): Out = HNil 
     } 

    implicit def hlistCoFilter1[L <: HList, H](implicit f: CoFilter[L, H]): Aux[H :: L, H, H :: f.Out] = 
     new CoFilter[H :: L, H] { 
     type Out = H :: f.Out 
     def apply(l: H :: L): Out = l.head :: f(l.tail) 
     } 

    implicit def hlistCoFilter2[H, L <: HList, U](implicit f: CoFilter[L, U], e: U <:!< H): Aux[H :: L, U, f.Out] = 
     new CoFilter[H :: L, U] { 
     type Out = f.Out 
     def apply(l: H :: L): Out = f(l.tail) 
     } 
    } 

    def covariantFilter[U](implicit filter: CoFilter[L, U]): filter.Out = filter(l) 
} 

object Main extends App { 

    class Foo(val foo: Int) 
    class Bar(val bar: Int) extends Foo(bar) 
    val l = new Foo(1) :: new Bar(2) :: new Foo(3) :: new Bar(4) :: HNil 
    implicit def hlistOps[L <: HList](l: L): HListOps[L] = new HListOps(l) 
    print(l.covariantFilter[Bar] != l) 

} 

Cung cấp cho tôi

[error] /tmp/rendererbI8Iwy0InO/src/main/scala/test.scala:47: could not find implicit value for parameter filter: _1.CoFilter[shapeless.::[Main.Foo,shapeless.::[Main.Bar,shapeless.::[Main.Foo,shapeless.::[Main.Bar,shapeless.HNil]]]],Main.Bar] 
[error] print(l.covariantFilter[Bar] != l) 
+1

Đây sẽ là một câu hỏi hay hơn nếu bạn giải thích những gì bạn đang cố gắng làm. Trong nháy mắt, tôi không thể nói lý do tại sao bạn mong đợi hoặc 'hlistCoFilterN' để cung cấp cho bạn ví dụ bạn muốn (không phải là trường hợp' Foo =: ​​= Bar' hoặc 'Bar <:!

+0

@TravisBrown điểm tốt. – Reactormonk

+0

Ah, bạn không muốn kiểm tra 'l.covariantFilter [Foo]', sau đó? –

Trả lời

6

Có một vài vấn đề ở đây. Đầu tiên là lớp loại của bạn được định nghĩa bên trong lớp mở rộng của bạn, nhưng bạn cần ví dụ tại điểm mà bạn đang gọi covariantFilter. Có lẽ trình biên dịch có thể tìm thấy nó cho bạn, nhưng nó không. Tuy nhiên, không cần phải làm sạch lớp học.

Vấn đề thứ hai là hai trường hợp hlistCoFilterN của bạn không thực sự nắm bắt tất cả nội dung bạn muốn. Bạn chỉ yêu cầu trình biên dịch phải làm gì trong trường hợp loại đầu là loại bộ lọc và nơi loại bộ lọc không phải là loại phụ của loại đầu. Điều gì về nơi mà loại đầu là một loại phụ của loại bộ lọc? Bạn có thể muốn một cái gì đó như thế này:.

import shapeless._ 

trait CoFilter[L <: HList, U] extends DepFn1[L] { type Out <: HList } 

object CoFilter { 
    def apply[L <: HList, U](implicit f: CoFilter[L, U]): Aux[L, U, f.Out] = f 

    type Aux[L <: HList, U, Out0 <: HList] = CoFilter[L, U] { type Out = Out0 } 

    implicit def hlistCoFilterHNil[L <: HList, U]: Aux[HNil, U, HNil] = 
    new CoFilter[HNil, U] { 
     type Out = HNil 
     def apply(l: HNil): Out = HNil 
    } 

    implicit def hlistCoFilter1[U, H <: U, T <: HList] 
    (implicit f: CoFilter[T, U]): Aux[H :: T, U, H :: f.Out] = 
     new CoFilter[H :: T, U] { 
     type Out = H :: f.Out 
     def apply(l: H :: T): Out = l.head :: f(l.tail) 
     } 

    implicit def hlistCoFilter2[U, H, T <: HList] 
    (implicit f: CoFilter[T, U], e: H <:!< U): Aux[H :: T, U, f.Out] = 
     new CoFilter[H :: T, U] { 
     type Out = f.Out 
     def apply(l: H :: T): Out = f(l.tail) 
     } 
} 

implicit final class HListOps[L <: HList](val l: L) { 
    def covariantFilter[U](implicit filter: CoFilter[L, U]): filter.Out = filter(l) 
} 

(Đối với hồ sơ, bạn cũng có thể loại bỏ các H <:!< U hạn chế và di chuyển hlistCoFilter2 đến một đặc điểm LowPriorityCoFilter tôi thấy phiên bản này một chút rõ ràng hơn về ý định của mình, nhưng loại bỏ các hạn chế cho là sẽ sạch hơn)

Bây giờ nếu bạn có điều sau đây:.

class Foo(val foo: Int) 
class Bar(val bar: Int) extends Foo(bar) 
val l = new Foo(1) :: new Bar(2) :: new Foo(3) :: new Bar(4) :: HNil 

lọc bạn sẽ làm việc như thế này:

scala> l.covariantFilter[Foo] == l 
res0: Boolean = true 

scala> l.covariantFilter[Bar] == l 
res1: Boolean = false 

Mà tôi nghĩ là những gì bạn muốn.

+0

Yêu cầu đối với 'hlistCoFilter2' có cần thiết không? Một nên mặc định anyway. – Reactormonk

+0

Bạn có nghĩa là ràng buộc '<:! <'? Không, nhưng bạn phải di chuyển 'hlistCoFilter2' thành đặc điểm' LowPriorityWhatever' để ưu tiên hai. –

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