5

I've constructed the following:

import shapeless._
import poly._

object Main {
    def main(args: Array[String]) = {

        object iterateOverHList extends (List ~> Iterator) {
            def apply[T](it: List[T]) = it.iterator
        }

        val x = List(1,2,3) :: List("cat","dog") :: HNil

        val xIt = x map iterateOverHList

    }
}

The above code works great and is awesome. However, I still want more. I would like to, rather than specifying that my HList will contain Lists, allow any Iterable. Like this:

import shapeless._
import poly._

object Main {
    def main(args: Array[String]) = {

        object iterateOverHList extends (Iterable ~> Iterator) {
            def apply[T](it: Iterable[T]) = it.iterator
        }

        val x = List(1,2,3) :: List("cat","dog") :: HNil

        val xIt = x map iterateOverHList

    }
}

This second version fails to compile, with the message "could not find implicit value for parameter mapper: shapeless.ops.hlist.Mapper[iterateOverHList.type,shapeless.::[List[Int],shapeless.::[List[String],shapeless.HNil]]]". The subtype polymorphism I'm expecting here, that a function that works on Iterable should work on List, is failing for some reason. Why is that? Is there a way for me to get around this problem, or will my own greed be my undoing?

jcrudy
  • 3,921
  • 1
  • 24
  • 31

1 Answers1

8

~> works with exact types. If you want a Poly1 for any subtype of Iterable you should create it like this:

object iterateOverHList extends Poly1 {
  implicit def iterable[T, L[T] <: Iterable[T]] = at[L[T]](_.iterator)
}

You could also create a Poly1 that works on some types that could be treated as Iterable like this:

import scala.collection.generic.IsTraversableOnce
object iterateOverHList extends Poly1 {
  implicit def iterable[L](implicit i: IsTraversableOnce[L]) =
    at[L](i.conversion(_).toIterator)
}

val x = "abc" :: List(1,2,3) :: HNil
val xIt = x map iterateOverHList
// xIt: shapeless.::[Iterator[Char],shapeless.::[Iterator[Int],shapeless.HNil]] = non-empty iterator :: non-empty iterator :: HNil
senia
  • 37,745
  • 4
  • 88
  • 129
  • Thanks! I used the first one. This solution caused a warning about enabling higher-kinded types, which was eliminated by importing scala.language.higherKinds. I'm not sure I actually understand the solution, though. I've seen "at" sprinkled throughout shapeless, but have been confused by it. What does it do, and do you know where it's defined? – jcrudy Jan 03 '14 at 06:06
  • @jcrudy: `where at is defined and how it works` is a good SO question. And the answer is too big for comment. You should ask it as SO question (don't forget to mention `apply` method of `~>`). You could even get an answer from [Miles Sabin](http://stackoverflow.com/users/146737/miles-sabin). – senia Jan 03 '14 at 12:08
  • @senia: How would the first solution work if type T must be bounded, e.g. iterable[T <: MyTrait, L[T] <: Iterable[T]]? It appears that it is shadowed by L[T] and the bound doesn't apply. – Costas Kotsokalis Mar 15 '17 at 21:26
  • @senia Hmm, turns out my problem is having to do with using a custom type as the upper bound for L[T]. Started a new question at http://stackoverflow.com/questions/42853858/shapeless-mapping-and-subtype-polymorphism-with-custom-type-bound – Costas Kotsokalis Mar 17 '17 at 09:37