In general, if you have any operation that you want to perform for all elements in an HList , you need to match the value of the polymorphic function over the HList . For example, suppose I have the following setting:
trait Listener[L <: Listener[L]] { def handle(s: String): Option[L] } class FooListener extends Listener[FooListener] { def handle(s: String) = if (s.size == 3) Some(this) else None } class BarListener extends Listener[BarListener ]{ def handle(s: String) = Some(this) } import shapeless._ val listeners = new FooListener :: new BarListener :: HNil
Now I want to send a String each of these listeners and collect the results. If I just wanted to send a fixed value, that would be easy:
object event123 extends Poly1 { implicit def listener[L <: Listener[L]] = at[L](_.handle("123")) } val result = listeners.map(event123)
which will be appropriately printed as Option[FooListener] :: Option[BarListener] :: HNil . If I use shapeless-contrib , I can execute the HList sequence:
import scalaz._, Scalaz._, shapeless.contrib.scalaz._ val sequenced: Option[FooListener :: BarListener :: HNil] = sequence(result)
Or just use traverse :
traverse(listeners)(event123)
Unfortunately, there are restrictions on how the values of a polymorphic function can be determined, which means that a partial application is not convenient, so if we do not know the String that we send at compile time, it is much more complicated
object event extends Poly1 { implicit def listener[L <: Listener[L]] = at[(L, String)] { case (listener, string) => listener.handle(string) } } traverse(listeners.zip(listeners.mapConst("123")))(event)
Where we pinned elements with a string, and then displayed a polymorphic function that takes tuples for the result. There are other ways to do this using more or less the same approach, but none of them are terribly clear.
A completely different approach is to simply skip the values of the polymorphic function and define a new class type:
trait Notifiable[L <: HList] { def tell(s: String)(l: L): Option[L] } object Notifiable { implicit val hnilNotifiable: Notifiable[HNil] = new Notifiable[HNil] { def tell(s: String)(l: HNil) = Some(HNil) } implicit def hconsNotifiable[H <: Listener[H], T <: HList](implicit tn: Notifiable[T] ): Notifiable[H :: T] = new Notifiable[H :: T] { def tell(s: String)(l: H :: T) = for { h <- l.head.handle(s) t <- tn.tell(s)(l.tail) } yield h :: t } } def tell[L <: HList: Notifiable](s: String)(l: L) = implicitly[Notifiable[L]].tell(s)(l)
And then:
val sequenced: Option[FooListener :: BarListener :: HNil] = tell("123")(listeners)
This is less general (it only works on Option , and not on arbitrary applications), but it does not require an additional dependency for sequencing, and it is probably a little less messy than jumping through hoops to partially apply a polymorphic value function due to strange compiler restrictions.