4
votes

Given the following

case class A(value:Int)
case class B(value:String)

val h:Option[A] :: A :: Option[B] :: Option[A] :: HNil = Some(A(1)) :: A(2) :: Some(B("two")) :: (None:Option[B]) :: HNil

How can I get the following ?

A(1) :: A(2) :: B("two") :: HNil

My attempt below

trait a extends Poly1 {
    implicit def any[T] = at[T](_ :: HNil)
}
object f extends a {            
    implicit def some[T] = at[Option[T]](t => if (t.isDefined) t.get :: HNil else HNil)         
}

works for map

h map f

> A(1) :: HNil :: A(2) :: HNil :: B(two) :: HNil :: HNil :: HNil

but fail for flatMap

h flatMap f

> could not find implicit value for parameter mapper: shapeless.ops.hlist.FlatMapper[f.type,shapeless.::[Option[A],shapeless.::[A,shapeless.::[Option[B],shapeless.::[Option[B],shapeless.HNil]]]]]
1
It's a little unclear what you're asking. What's f? What does the argument to flatMap look like? Also as defined h is going to be statically typed as having Some[A], etc. elements, which is pretty much never useful.Travis Brown

1 Answers

1
votes

Most likely the only thing you can do is define separate cases for Some and for None:

trait a extends Poly1 {
  implicit def default[T] = at[T](_ :: HNil)
}
object f extends a {
  implicit def caseSome[T] = at[Some[T]](_.get :: HNil)
  implicit def caseNone = at[None.type](_ => HNil)
}

It also means you can't use generic Options in the types, it must be known at compile time whether each element is Some or None:

scala> (Some(A(1)) :: A(2) :: Some(B("two")) :: None :: HNil) flatMap f
res1: shapeless.::[A,shapeless.::[A,shapeless.::[B,shapeless.HNil]]] = A(1) :: A(2) :: B(two) :: HNil

This distinction defines the type of the resulting expression: Some(1) :: HNil flatMap f will have type ::[Int, HNil], but None :: HNil flatMap f will have type just HNil.

This kind of type information can't be figured out at compile time from simple Options: should (x: Option[T]) :: HNil flatMap f have type ::[T, HNil] or HNil? We don't know until we actually run the program and see what the value of x is.

I'm not sure if there's some smart way to do it anyway and get an opaque HList, but at that point you'll abandon exact type information about each element and the length of the list and may as well cast it to a normal List (and maybe use cast from shapeless later if you know what exact type the final result will have)