Behavior of shapeless .toHList - scala

The following compiles:
object Run1 extends App {
import shapeless._
import syntax.std.traversable._
case class Container[T](x: T)
Seq(Container(1), Container("x")).toHList[Container[Int] :: Container[String] :: HNil]
}
But this does not:
object Run2 extends App {
import shapeless._
import syntax.std.traversable._
class Container[T](val x: T)
Seq(new Container(1), new Container("x")).toHList[Container[Int] :: Container[String] :: HNil]
}
It fails with the following errors:
Error:(40, 52) could not find implicit value for parameter fl: shapeless.ops.traversable.FromTraversable[shapeless.:: [com.adaje.service.table.Run2.Container[Int],shapeless.::[com.adaje.service.table.Run2.Container[String],shapeless.HNil]]]
Seq(new Container(1), new Container("x")).toHList[Container[Int] :: Container[String] :: HNil]
^
Why does the second program not work and is there anything that can be added so that it does?
Thanks

The FromTraversable type class requires Typeable instances for the element types. Shapeless provides these off the shelf for case classes, but not for arbitrarily defined classes. You can define your own pretty easily, though:
import shapeless._, shapeless.syntax.std.traversable._
class Container[T](val x: T)
implicit def containerTypeable[A: Typeable]: Typeable[Container[A]] =
new Typeable[Container[A]] {
def cast(t: Any): Option[Container[A]] = t match {
case c: Container[_] => Typeable[A].cast(c.x).map(new Container(_))
case _ => None
}
def describe: String = s"Container[${ Typeable[A].describe }]"
}
And then:
scala> val cs = Seq(new Container(1), new Container("x"))
cs: Seq[Container[_ >: String with Int]] = List(Container#3c3c89b2, Container#1273a053)
scala> cs.toHList[Container[Int] :: Container[String] :: HNil]
res0: Option[shapeless.::[Container[Int],shapeless.::[Container[String],shapeless.HNil]]] = Some(Container#357c808b :: Container#607bcaf5 :: HNil)
And also:
scala> cs.reverse.toHList[Container[Int] :: Container[String] :: HNil]
res1: Option[shapeless.::[Container[Int],shapeless.::[Container[String],shapeless.HNil]]] = None
scalac's -Xlog-implicits option can be handy in cases like this—it'll make it clear that what was missing in your original non-case-class attempt was the Typeable instances.

Related

Mapping over generic tuples with polymorphic functions

Scala 3 provides polymorphic functions and Tuples similar to shapeless HList:
scala> 1 *: "foo" *: Tuple()
val res0: (Int, String) = (1,foo)
scala> val f: ([T] => T => Option[T]) = [T] => (v: T) => Some(v)
val f: PolyFunction{apply: [T](x$1: T): Option[T]} = <function1>
scala> res0.map(f)
val res1: Option[Int] *: Option[String] *: EmptyTuple = (Some(1),Some(foo))
How could we reimplement the following shapeless example using Scala 3 functionality?
import poly._
object choose extends (Set ~> Option) {
def apply[T](s : Set[T]) = s.headOption
}
scala> val sets = Set(1) :: Set("foo") :: HNil
sets: Set[Int] :: Set[String] :: HNil = Set(1) :: Set(foo) :: HNil
scala> val opts = sets map choose
opts: Option[Int] :: Option[String] :: HNil = Some(1) :: Some(foo) :: HNil
In other words, how could we make something like so compile
scala> val choose: ([T] => Set[T] => Option[T]) = [T] => (s: Set[T]) => s.headOption
val choose: PolyFunction{apply: [T](x$1: Set[T]): Option[T]} = <function1>
scala> val sets = Set(1) *: Set("foo") *: Tuple()
val sets: (Set[Int], Set[String]) = (Set(1),Set(foo))
scala> sets.map(choose)
1 |sets.map(choose)
| ^^^^^^
| Found: (choose : PolyFunction{apply: [T](x$1: Set[T]): Option[T]})
| Required: PolyFunction{apply: [t](x$1: t): Any}
Shapeless map is quite a bit more magical than Scala 3 tuple map, the signature of latter being:
def map[F[_]](f: [t] => (x$1: t) => F[t]): Map[Tuple, F]
Map[Tuple, F] is a special match type that is basically tuple with every argument type wrapped in F[_]. The issue is that shape, t => F[t] that prevents too much fanciness.
Except of course, F can be a match type itself:
type Choose[T] = T match {
case Set[a] => Option[a]
}
def choose[T](t: T): Choose[T] = t match
case set: Set[a] => set.headOption
// messy, but it works
#main def run =
val sets = Set(1) *: Set("foo") *: Tuple()
println(sets.map([T] => (t: T) => choose(t)))
There's currently an issue that compiler won't infer polymorphic function from methods. Match types aren't fully type-safe either, e.g. doing choose("string") will compile but throw a MatchError. I've also ran into problems with inference of a match type from polymorphic function value, hence the usage of def method.

Getting elements from Slick HLIST (or Convert Slick HLIst into Shapeless HList)

I have auto-generated scala code using slick codegen. I see that some tables Rows are implements as HLists. (but these are slick HList, not the normal shapeless HList)
Now I want a specific element from the HList returned as a Row by the slick query.
I googled and found this thread
Getting elements from an HList
But this does not work for slick HList. it works very well for Shapeless HList
I also tried the apply method
val x : Long = slickHList(2)
but this doesn't compile because type Any does not conform to exected type of Long. I would hate to do a .asInstanceOf
Is there a typesafe way in which I can access the elements of the slick HList?
Edit: Based on the input below I wrote the code below
package com.abhi
object SlickAndShapeless {
import slick.collection.heterogeneous.{HCons, HList, HNil}
import slick.collection.heterogeneous.syntax.HNil
type MyRow = HCons[Long, HCons[String, HNil]]
val row : MyRow = 1L :: "foo" :: HNil
import HListExtensions._
val hlist = row.asShapeless
val container = new Container(hlist)
val item = container.get(1)
}
class Container[L <: shapeless.HList](list: L) {
import shapeless._
import nat._
import ops.hlist._
def get(n: Nat)(implicit at: At[L, n.N]): at.Out = list[n.N]
}
object HListExtensions {
import slick.collection.heterogeneous.{HNil => SHNil, HList => SHList, HCons}
import shapeless.{::, HList, HNil}
implicit class HListShapelessSlick(val list: HList) extends AnyVal {
def asSlick : SHList = list match {
case HNil => SHNil
case head :: tail => head :: tail.asSlick
}
}
implicit class HListSlickShapeless(val list: SHList) extends AnyVal {
def asShapeless : HList = list match {
case SHNil => HNil
case HCons(head, tail) => head :: tail.asShapeless
}
}
}
The problem with the code above is that the type of item obtained from val item = container.get(1) is at.Out and not Long as I was expecting.
build.sbt
libraryDependencies ++= Seq(
"com.typesafe.slick" % "slick_2.12" % "3.2.1",
"com.chuusai" % "shapeless_2.12" % "2.3.2"
)
I also see two compiler errors
Error:(19, 35) Implicit not found: shapeless.Ops.At[shapeless.HList, shapeless.Succ[shapeless._0]]. You requested to access an element at the position shapeless.Succ[shapeless._0], but the HList shapeless.HList is too short.
val item : Long = container.get(1)
Error:(19, 35) not enough arguments for method get: (implicit at: shapeless.ops.hlist.At[shapeless.HList,shapeless.Succ[shapeless._0]])at.Out.
Unspecified value parameter at.
val item : Long = container.get(1)
It's possible to create extension methods:
object HListExtensions {
import slick.collection.heterogeneous.{HNil => SHNil, HList => SHList, HCons}
import shapeless.{ ::, HList, HNil }
implicit class HListShapelessSlick(val list:HList) extends AnyVal {
def asSlick:SHList = list match {
case HNil => SHNil
case head :: tail => head :: tail.asSlick
}
}
implicit class HListSlickShapeless(val list:SHList) extends AnyVal {
def asShapeless:HList = list match {
case SHNil => HNil
case HCons(head, tail) => head :: tail.asShapeless
}
}
}
Example:
scala>import HListExtensions._
import HListExtensions._
scala> val x1:HList = 1 :: 2 :: HNil
x1: slick.collection.heterogeneous.HList = 1 :: 2 :: HNil
scala> x1.asShapeless
res1: shapeless.HList = 1 :: 2 :: HNil
scala> x1.asShapeless.asSlick
res2: slick.collection.heterogeneous.HList = 1 :: 2 :: HNil
I hope this helps.
Edit: Here is type level solution.
object HListsConvertersTypeLevel {
import shapeless.{::}
sealed trait HConv[From <: heterogeneous.HList, To <: shapeless.HList] {
def convert(list: From): To
}
implicit def buildHConvNil: HConv[heterogeneous.HNil.type, shapeless.HNil] =
new HConv[heterogeneous.HNil.type, shapeless.HNil] {
override def convert(list: heterogeneous.HNil.type): shapeless.HNil = shapeless.HNil
}
implicit def buildHConv[H, T <: heterogeneous.HList, T2 <: shapeless.HList](
implicit conv: HConv[T, T2]): HConv[HCons[H, T], ::[H, T2]] = new HConv[HCons[H, T], ::[H, T2]] {
override def convert(list: HCons[H, T]): ::[H, T2] = {
list.head :: conv.convert(list.tail)
}
}
def toShapeless[A <: heterogeneous.HList, B <: shapeless.HList](list: A)(implicit conv: HConv[A, B]): B = conv.convert(list)
}
Example:
object SlickAndShapeless {
import slick.collection.heterogeneous.{HCons, HNil}
import slick.collection.heterogeneous.syntax.HNil
type MyRow = HCons[Long, HCons[String, HNil]]
val row: MyRow = 1L :: "foo" :: HNil
import HListsConvertersTypeLevel._
val hlist = toShapeless(row)
val item: Long = hlist.head
val item2: String = hlist.tail.head
}

Parse List[String] into HList

I would like to write def parse[T <: HList](list: List[String]): Validation[T]. list could be List("fooid", "barid"), and T FooId :: BarId :: HNil, and a typeclass Parse[T] which implements String => Validation[FooId]. How would I write said parse, which parses the list into T? I'm not sure how to summon the implicit typeclasses for each of the elements of T.
We can adapt the code from shapeless.ops.traversable.FromTraversable.
I'm not sure what your Validation type is, but I used it as an alias for scalaz.ValidationNel[String, A] below (mostly so I could use the string syntax to easily give me some Parse instances).
import scalaz.{Validation => _, _}, Scalaz._
type Validation[A] = ValidationNel[String, A]
trait Parse[T] {
def apply(s: String): Validation[T]
}
object Parse {
def fromScalazParse[E <: Exception, T](f: String => scalaz.Validation[E, T]) =
new Parse[T] {
def apply(s: String): Validation[T] =
f(s).leftMap(_.getMessage).toValidationNel
}
implicit val booleanParse = fromScalazParse(_.parseBoolean)
implicit val intParse = fromScalazParse(_.parseInt)
implicit val doubleParse = fromScalazParse(_.parseDouble)
}
With the Parser type class sorted, we can now create a type class based on FromTraversable to parse a List[String] and give us a Validation[A :: B :: HNil] :
import shapeless._
import scala.collection.GenTraversable
trait FromTraversableParsed[Out <: HList] extends Serializable {
def apply(l: GenTraversable[String]) : Validation[Out]
}
object FromTraversableParsed {
def apply[Out <: HList](implicit from: FromTraversableParsed[Out]) = from
implicit val hnilFromTraversableParsed =
new FromTraversableParsed[HNil] {
def apply(l: GenTraversable[String]): Validation[HNil] =
if(l.isEmpty) HNil.successNel[String]
else "Traversable is not empty".failureNel[HNil]
}
implicit def hlistFromTraversableParsed[OutH, OutT <: HList](implicit
ftpT: FromTraversableParsed[OutT],
parseH: Parse[OutH]
): FromTraversableParsed[OutH :: OutT] =
new FromTraversableParsed[OutH :: OutT] {
def apply(l : GenTraversable[String]) : Validation[OutH :: OutT] =
if(l.isEmpty) "Empty traversable".failureNel[OutH :: OutT]
else (parseH(l.head) |#| ftpT(l.tail))(_ :: _)
}
}
We can add some syntax to make using FromTraversableParsed a little bit easier :
implicit class ParseStringListOps(val strings: List[String]) extends AnyVal {
def parse[L <: HList](implicit ftp: FromTraversableParsed[L]): Validation[L] =
ftp(strings)
}
Now we can do :
List("1", "true", "3.0").parse[Int :: Boolean :: Double :: HNil]
// Validation[Int :: Boolean :: Double :: HNil] = Success(1 :: true :: 3.0 :: HNil)

Class method implicit param cannot be found when not calling from a val

I've got a class that depends on an HList, has a polymorphic function and a method to map this function over the list. The strange thing is that this method can only be invoked on a val, otherwise the compilation fails:
import shapeless._
import shapeless.ops.hlist.Mapper
class C[L <: HList](l: L) {
object f extends Poly1 {
implicit def whatever[T] = at[T]{_ => ()}
}
def g(implicit m: Mapper[f.type,L]) = ()
}
val c = new C(1 :: "s" :: HNil)
// this line compiles:
val v1 = c.g
// compilation fails if c is inlined:
val v2 = (new C(1 :: "s" :: HNil)).g
// error: could not find implicit value for parameter m:
// Mapper[_1.f.type, Int :: String :: HNil]]]
Is there a way to overcome such behaviour while keeping f inside C? I've tried to extract the method g to another class or object, but it didn't help.

How to return multiple variants of HList from a function?

Having
class A
class B extends A
class C extends A
class Container[+L <: HList](l: L)
what is shapeless way to code as follows?:
def foo[L <: HList](a: A): Container[L] = a match {
case (b: B) => new Container(1 :: "a" :: HNil)
case (c: C) => new Container(1.0 :: HNil)
case _ => new Container(HNil)
}
and then to use it in a way:
val l1: Container[Int :: String :: HNil] = foo(new B)
val l2: Container[Double :: HNil] = foo(new C)
val l3: Container[String :: HNil] = foo(new C) // Compile-time error
NOTE that the way above is principally incorrect because of reasons similar to ones described at "Why `List[B]` is not a subtype of `Seq[L]` when `class B extends A` and `L <: A`?".
You can use shapeless' polymorphic functions for this:
// Base case
class LowPrioFoo extends Poly1 {
implicit def default[X] = at[X] { _ => new Container(HNil) }
}
// Specific cases
object foo extends LowPrioFoo {
implicit def atB = at[B] { _ => new Container(1 :: "a" :: HNil) }
implicit def atC = at[C] { _ => new Container(1.0 :: HNil) }
}
Now you can call the function foo on whatever you need:
val x = foo(new A): Container[HNil]
val y = foo(new B): Container[Int :: String :: HNil]
val z = foo(new C): Container[Double :: HNil]
This is essentially the same than you did, but it is encapsulated by foo (and interfaces nicer with shapeless). This way you can make sure no unintended conversion happens.
ADDENDUM
As #MilesSabin pointed out, there is not much use for shapeless' polymorphic functions, if the value of the argument is not used. A simple type class based solution is probably better. Such a solution is given as follows:
trait Foo[T] {
type R
def result: R
}
trait LowPrioFoo {
implicit def default[X] = new Foo[X] {
type R = Container[HNil]
val result = new Container(HNil)
}
}
object Foo extends LowPrioFoo {
implicit val bFoo = new Foo[B] {
type R = Container[Int :: String :: HNil]
val result = new Container(1 :: "a" :: HNil)
}
implicit val cFoo = new Foo[C] {
type R = Container[Double :: HNil]
val result = new Container(1.0 :: HNil)
}
}
def foo[A](x: A)(implicit f: Foo[A]): f.R = f.result
Note that this is already very close to the inner workings of Poly. Compare trait Foo to traits CaseAux and Poly#Case which model parameters as HList and allow result to depend on the actual value. This makes Foo a special case of these type classes.
Finally understood the problem essence... Seems like as follows:
implicit def fromA(a: A): Container[HNil] = new Container(HNil)
implicit def fromB(b: B): Container[Int :: String :: HNil] = new Container(1 :: "a" :: HNil)
implicit def fromC(c: C): Container[Double :: HNil] = new Container(1.0 :: HNil)
Then it is eligible to write:
val l1: Container[Int :: String :: HNil] = new B
val l2: Container[Double :: HNil] = new C
// val l3: Container[String :: HNil] = new C // Compile-time error
val l4: Container[HNil] = new A
Any better solutions are welcome.