2014-09-07 2 views
5

Я намерен фильтровать на HList ковариантным образом - я хотел бы также включить подклассы. Поэтому ковариантный фильтр на Foo должен захватывать элементы Foo, а также Bar. Я построил этот пример, попробовав <:!<, чтобы узнать, делает ли он то, что я хотел бы сделать.Сделайте ковариантный фильтр на 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) 

} 

дает мне

[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

Это был бы гораздо лучший вопрос, если бы вы объяснили, что вы пытаетесь сделать. С первого взгляда я не могу сказать, почему вы ожидаете, что либо «hlistCoFilterN» предоставит вам нужный вам экземпляр (это не тот случай, когда «Foo =: ​​= Bar» или «Bar <:!

+0

@TravisBrown хорошая точка. – Reactormonk

+0

Ах, разве вы не хотите проверять 'l.covariantFilter [Foo]', тогда? –

ответ

6

Есть несколько вопросов здесь. Во-первых, ваш тип класса определяется внутри вашего класса расширения, но вам нужен экземпляр в точке, где вы вызываете covariantFilter. Может быть, компилятор может найти его для вас, но это не так. Тем не менее, гораздо чище не гнездовать класс типа.

Вторая проблема заключается в том, что ваши два корпуса hlistCoFilterN фактически не захватывают все, что вам нужно. Вы сообщаете только компилятору, что делать в случаях, когда тип головы является типом фильтра и где тип фильтра не является подтипом типа головы. Как насчет того, где тип головы является подтипом типа фильтра? Вы, наверное, хотите что-то вроде этого:.

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) 
} 

(Для записи, вы можете также удалить H <:!< U ограничение и двигаться hlistCoFilter2 к LowPriorityCoFilter черте я нахожу эту версию немного понятнее о своем намерении, но избавившись от ограничение будет возможно чище)

Теперь, если у вас есть следующее:.

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 

Ваш фильтр будет работать следующим образом:

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

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

Что я думаю, это то, что вы хотите.

+0

Требуется ли требование для 'hlistCoFilter2'? По умолчанию все равно. – Reactormonk

+0

Вы имеете в виду ограничение '<:! <'? Нет, но вам нужно переместить 'hlistCoFilter2' в свойство« LowPriorityWhatever », чтобы определить приоритеты двух. –

Смежные вопросы