0

I am new to shapeless (and still low level in the learning curve of scala...) and i have some hard time with shapeless

import shapeless._
case class FooBar[T](foo: String, bar: T)
val hl = 0 :: FooBar("A", "one") :: FooBar("B", 1) :: "0" :: FooBar("C", "two") :: HNil
val l = hl.filter[FooBar[String]].toList
println(l) //List(FooBar(A,one), FooBar(C,two))

It works fine

Next step, i want to put that in function, something like

def filter[T](hl: HList): List[FooBar[T]] = ???

so i can simplify calling to

filter[String](hl)
filter[Int](hl)

naively i tested

def filter[T](hl: HList): List[FooBar[T]] = {
  hl.filter[FooBar[T]].toList
}

which give

 could not find implicit value for parameter partition: shapeless.ops.hlist.Partition[shapeless.HList,FooBar[T]]

after some tries playing with implicit, i still have not found the correct way to do that

Do you have any idea ?

Thank you !

Johann
  • 25
  • 4

1 Answers1

1

If you lack some implicits then in your method you should suppose they are provided. Saying that an argument of the method is of type just HList (and not some specific L <: HList) is too rough.

Since probably you would like to specify T and not specify L (expecting that L will be inferred) try a type class + extension method

import shapeless._
import shapeless.ops.hlist.{Partition, ToTraversable}

case class FooBar[T](foo: String, bar: T)
val hl = 0 :: FooBar("A", "one") :: FooBar("B", 1) :: "0" :: FooBar("C", "two") :: HNil

trait FilterFooBar[L <: HList, T] {
  def apply(l: L): List[FooBar[T]]
}

object FilterFooBar {
  implicit def mkFilterFooBar[L <: HList, T, Prefix <: HList, Suffix <: HList](implicit
    partition: Partition.Aux[L, FooBar[T], Prefix, Suffix],
    toTraversable: ToTraversable.Aux[Prefix, List, FooBar[T]]
  ): FilterFooBar[L, T] = _.filter.toList    
}

implicit class FilterFooBarOp[L <: HList](l: L) {
  def filterFooBar[T](implicit filterFooBarInstance: FilterFooBar[L, T]): List[FooBar[T]] = 
    filterFooBarInstance(l)
}

println(hl.filterFooBar[String]) // List(FooBar(A,one), FooBar(C,two))
println(hl.filterFooBar[Int]) // List(FooBar(B,1))
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • As someone who loves shapeless, it makes me sad that doing this is so complicated. It would really be great if there was a way to specify only one type argument (and leave the rest inferred), so that it wouldn't be necessary to make a new typeclass whose sole purpose is to close over `Prefix` and `Suffix`. +1 – Alec Nov 16 '18 at 04:24
  • thank you ! Indeed there is quite much code, but i did not even think to use typeclass. Nice "trick" which may be useful for other case – Johann Nov 16 '18 at 19:39
  • Well, the code may look scary but every step was easy. Compiler said that it couldn't find `Partition`, so I added this implicit to your method. Then the same was with `ToTraversable`. I looked at definitions of `Partition` and `ToTraversable` and specified their type parameters correspondingly (and added generics to your method). Then I realized that it would be necessary to specify both `T` and `L` (and other generics) upon a call and replaced your method with type class + extension method so that `L` will be inferred and only `T` should be specified. – Dmytro Mitin Nov 16 '18 at 20:49