Why does mapping over an HList of Option[T] not work?
Asked Answered
C

1

6

This does not compile and I do not understand why:

import shapeless._
import poly._

object option extends (Option ~> List) {
    def apply[T](t: Option[T]) = t.toList
}

val simple = Some(1) :: Some("hello") :: Some(true) :: HNil
val opts: List[Int] :: List[String] :: List[Boolean] :: HNil = simple map option

This is somewhat surprising, because this does compile:

import shapeless._
import poly._

object choose extends (Set ~> Option) {
    def apply[T](s: Set[T]) = s.headOption
}

val sets = Set(1) :: Set("foo") :: HNil
val opts: Option[Int] :: Option[String] :: HNil = sets map choose
Childe answered 30/9, 2014 at 18:22 Comment(3)
Please say what error you get. Don't make the many readers (to your one writer) guess, or have to run it.Tiemannite
@Paul Thx for the suggestion. It's somewhat of a trade off between question conciseness (which vastly improves the chances someone will actually answer the question, IMHO) and providing as much information as possible. Maybe one day, SO will have an integrated compiler and we will have the best of both worlds (not to mention less typos in code).Childe
Er, no, not in this case. Providing the error message is a few extra lines, and does not clutter what you already put. "does not compile" is only marginally less worse than the infamous "it doesn't work"Tiemannite
C
6

The problem is your usage of Some instead of Option. The compiler complains that it can't find anything to convert a list of Somes though your Option mapper is being used. To fix this you can:

Change your simple list construction to use Option instead of Some:

  object option extends (Option ~> List) {
    def apply[T](t: Option[T]) = t.toList
  }

  val simple = Option(1) :: Option("hello") :: Option(true) :: HNil
  val opts2: List[Int] :: List[String] :: List[Boolean] :: HNil = simple map option

Use Some as the type of your mapper:

  object option extends (Some ~> List) {
    def apply[T](t: Some[T]) = t.toList
  }

  val simple = Some(1) :: Some("hello") :: Some(true) :: HNil
  val opts2: List[Int] :: List[String] :: List[Boolean] :: HNil = simple map option

Or force the type of simple to be downcast to Option:

  object option extends (Option ~> List) {
    def apply[T](t: Option[T]) = t.toList
  }

  val simple: Option[Int] :: Option[String] :: Option[Boolean] :: HNil = Some(1) :: Some("hello") :: Some(true) :: HNil
  val opts2: List[Int] :: List[String] :: List[Boolean] :: HNil = simple map option
Choir answered 30/9, 2014 at 18:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.