Convert Sized[Something[String], _] collection to HList

时间:2015-06-30 13:25:14

标签: scala shapeless

I'm new to shapeless and trying to learning it by doing. I want to make a very small library which can convert a collection (in first step a Sized collection) of String, to HList of different types.

basically what I want to achieve:

import shapeless._
import nat._
import BigQueryParser._

val s: Sized[IndexedSeq[String], nat._3] = Sized("Testing", "2.0", "1")

BigQueryParser[Sized[IndexedSeq[String], nat._3], String :: BigDecimal :: BigInt :: HNil].parse(s)

My non-working implementation is here https://gist.github.com/taojang/f6a9352dbc618039e3a3

I implemented it after https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/csv.scala

My code does not compile, compiler complains about following errors:

[error] /somepath/some-file.scala: could not find implicit value for parameter st: exmaple.BigQueryParser[shapeless.Sized[IndexedSeq[String],shapeless.nat._3],shapeless.::[String,shapeless.::[BigDecimal,shapeless.::[BigInt,shapeless.HNil]]]]
[error]   BigQueryParser[Sized[IndexedSeq[String], nat._3], String :: BigDecimal :: BigInt :: HNil].parse(s)

1 个答案:

答案 0 :(得分:6)

I find that the best way to debug these kinds of issues is to try to build your instances by hand. For example, this is fine:

scala> BigQueryParser[Sized[IndexedSeq[String], _0], HNil]
res0: BigQueryParser[shapeless.Sized[IndexedSeq[String],shapeless.nat._0],shapeless.HNil] = BigQueryParser$$anon$6@2f4cd46d

But this breaks:

scala> deriveHCons[IndexedSeq[String], _0, String, HNil]
<console>:31: error: could not find implicit value for parameter conv: BigQueryParser[scala.collection.generic.IsTraversableLike[IndexedSeq[String]]#A,String]
       deriveHCons[IndexedSeq[String], _0, String, HNil]
                  ^

Which suggests that something is going wrong with the IsTraversableLike[Repr]#A type projection. The first thing I'd do in a situation like that is make it a type parameter (ReprA in this case) and then constrain the IsTraversableLike[Repr] instance with a refinement type:

implicit def deriveHCons[Repr, ReprA, L <: Nat, V, T <: HList](implicit
  itl: IsTraversableLike[Repr] { type A = ReprA },
  ev: AdditiveCollection[Repr],
  ts: BigQueryParser[Sized[Repr, L], T],
  conv: BigQueryParser[ReprA, V]
): BigQueryParser[Sized[Repr, Succ[L]], V :: T] =
  new BigQueryParser[Sized[Repr, Succ[L]], V :: T] {
    def parse(s: Sized[Repr, Succ[L]]): Try[V :: T] =
      for {
        h <- conv.parse(s.head)
        t <- ts.parse(s.tail)
      } yield h :: t
  }

And that works just fine:

scala> println(
     |   BigQueryParser[
     |     Sized[IndexedSeq[String], nat._3],
     |     String :: BigDecimal :: BigInt :: HNil
     |   ].parse(s)
     | )
Success(Testing :: 2.0 :: 1 :: HNil)

There may be other simplifications you could make (e.g. using _0 directly instead of a L <: _0 type parameter), but this fix should at least get things rolling.