The following code, which is taken from Apocalisp's excellent blog series:
Type level programming in scala , and modified for an implicit parsing scenario. However, this does not compile, with the following message:
error: ambiguous implicit values:
both method hParseNil in object HApplyOps of type => (com.mystuff.bigdata.commons.collections.hlist.HNil) => com.mystuff.bigdata.commons.collections.hlist.HNil
and method conforms in object Predef of type [A]<:<[A,A]
match expected type (com.mystuff.bigdata.commons.collections.hlist.HNil) => com.amadesa.bigdata.commons.collections.hlist.HNil
val l = hparse[HNil,HNil](HNil)
Can someone please explain why this happens, and if it's fixable?
sealed trait HList
final case class HCons[H, T <: HList](head: H, tail: T) extends HList {
def :+:[T](v: T) = HCons(v, this)
}
sealed class HNil extends HList {
def :+:[T](v: T) = HCons(v, this)
}
object HNil extends HNil
// aliases for building HList types and for pattern matching
object HList {
type :+:[H, T <: HList] = HCons[H, T]
val :+: = HCons
}
object HApplyOps
{
import HList.:+:
implicit def hParseNil: HNil => HNil = _ => HNil
implicit def hParseCons[InH,OutH,TIn <:HList,TOut<:HList](implicit parse:InH=>OutH,parseTail:TIn=>TOut): (InH :+: TIn) => (OutH :+: TOut) =
in => HCons(parse(in.head),parseTail(in.tail))
def hparse[In <: HList, Out <: HList](in:In)(implicit parse: In => Out):Out = in
}
object PG {
import HList._
def main(args: Array[String]) {
import HApplyOps._
val l = hparse[HNil,HNil](HNil)
}
}
Although i can't tell you exactly the purpose of Predef.conforms, i can tell you the ambiguity error seems correct (unfortunately). In the comment in the source it even says that <:< was introduced because of ambiguity problems of Function1 (says Function2 but i guess that is a mistake). But since <:< is a subclass of Function1 it can be passed in whenever Function1 is expected, so in your case its possible to pass in <:< to hparse.
Now the implicit def conforms[A]: A <:< A has the effect (from what i understand) that whenever a method expects a type A => A, it is sufficient to have an implicit value of A in scope.
In your case the implicit def hParseNil: HNil => HNil has the same priority as conforms and thus both could be equally applied.
I see two possible solutions:
just remove the hParseNil, i think your code still works.
shadow the Predef's conforms by naming yours the same:
implicit def conforms: HNil => HNil = _ => new HNil
You can just replace the function literal hParseNil to a normal function.
implicit def hParseNil(a:HNil): HNil = HNil
instead of
implicit def hParseNil: HNil => HNil = _ => HNil
Related
I'm working with Slick's GetResult typeclass and wanted to use Shapeless to derive instances of GetResult[Option[(A, B, C...)]]
What I want:
Given an implicit GetResult[Option[A]], GetResult[Option[B]], ...,
implicitly generate a GetResult[Option[(A, B, ...)]]
What I tried
trait CanGetOption[T] {
def getOption: GetResult[Option[T]]
}
object CanGetOption {
// convenience implicit resolver
def apply[T](implicit canGetOption: CanGetOption[T]): CanGetOption[T] = canGetOption
// base case: HNil
implicit val getHNilOption: CanGetOption[HNil] = from(GetResult { _ => Some(HNil) })
// recursion case: H :: Tail
implicit def getHConsOption[H, Tail <: HList](
implicit getHeadOption: GetResult[Option[H]],
canGetTailOption: CanGetOption[Tail]
): CanGetOption[H :: Tail] = from(GetResult[Option[H :: Tail]] { r =>
val headOpt = getHeadOption(r)
val tailOpt = canGetTailOption.getOption(r)
for(head <- headOpt; tail <- tailOpt) yield head :: tail
})
// generic case: A, given a A <-> Repr conversion
// I also tried moving this into a "LowPriorityImplicits" thing, just in case
implicit def getGenericOption[A, Repr <: HList](
implicit gen: Generic.Aux[A, Repr],
getReprOpt: CanGetOption[Repr]
): CanGetOption[A] = from(GetResult { r =>
val reprOpt = getReprOpt.getOption(r)
reprOpt.map(gen.from)
})
}
implicit def resolveOptionGetter[T: CanGetOption]: GetResult[Option[T]] =
CanGetOption[T].getOption
Problem:
When I've imported the above, the resolveOptionGetter doesn't seem to be considered when searching for implicits:
scala> implicitly[GetResult[Option[(Int, Int)]]]
<console>:19: error: could not find implicit value for parameter e: scala.slick.jdbc.GetResult[Option[(Int, Int)]]
implicitly[GetResult[Option[(Int, Int)]]]
^
scala> resolveOptionGetter[(Int, Int)]
res1: scala.slick.jdbc.GetResult[Option[(Int, Int)]] = <function1>
Why can't the compiler find resolveOptionGetter in the implicit search? What can I do to help it?
The thing is that slick.jdbc.GetResult is covariant. If it were invariant, types would be inferred correctly and implicits would be resolved.
A workaround is hiding covariant slick.jdbc.GetResult with custom invariant type alias GetResult. Remove import slick.jdbc.GetResult and write in your source file
type GetResult[T] = slick.jdbc.GetResult[T]
object GetResult {
def apply[T](implicit f: PositionedResult => T): GetResult[T] = slick.jdbc.GetResult.apply
}
Now implicitly[GetResult[Option[(Int, Int)]]] compiles. Tested in Scala 2.12.7 + Shapeless 2.3.3 + Slick 3.2.3.
Variance often makes troubles for implicit resolution:
https://github.com/scala/bug/issues/10099
https://github.com/locationtech/geotrellis/issues/1292
Implicit resolution with covariance
Note: I am learning shapeless, so please ask for clarification if I miss any details.
Background:
I am building an encoding/decoding solution for fixed-length format while practising Shapeless. The idea is that each case class would have its own encoder/decoder defined as an HList aligned with its properties.
Even if two classes share the same properties, their encoding may be different. The description for each field would contain some(4) values. But this is not important in the problem.
Problem:
The full code is available here: https://github.com/atais/Fixed-Length/blob/03b395947a6b00e548ea1e76a9660e471f136565/src/main/scala/test/Main.scala
I declare a sample case class and its encoder:
case class Employee(name: String, number: Int, manager: Boolean)
object Employee {
implicit val employeeEncoder =
FLEncoder.fixed((s: String) => s) ::
FLEncoder.fixed((s: String) => s) ::
FLEncoder.fixed((s: Int) => s.toString) ::
FLEncoder.fixed((s: Boolean) => s.toString) ::
HNil
}
so the type of my employeeEncoder is a beautiful:
::[FLEncoder[String], ::[FLEncoder[String], ::[FLEncoder[Int], ::[FLEncoder[Boolean], HNil]]]]
Now, encoder implicitly is looking for an FLEncoder[Employee], which I hope could be the above implementation.
I have used this solution to combine TypeClasses for tuples:
Composing typeclasses for tuples in Scala
https://www.scala-exercises.org/shapeless/auto_typeclass_derivation
But I am getting:
Error:(69, 17) could not find implicit value for parameter enc: test.FLEncoder[test.Employee]
println(encode(example))
If I declare those encoders separately, they are working fine
implicit val a = fixed((s: String) => s)
implicit val b = fixed((s: Int) => s.toString)
implicit val c = fixed((s: Boolean) => s.toString)
Question:
So basically, how to use Shapeless so it would know that this Hlist is an encoder type for an aligned case class?
A Similar problem is solved in scodec. If you check a demo here:
https://github.com/atais/Fixed-Length/blob/03b395947a6b00e548ea1e76a9660e471f136565/src/main/scala/test/Fixed.scala
you are able to do such transformation:
case class Person(name: String, age: Int)
val pc: Codec[shapeless.::[String, shapeless.::[Int, HNil]]] = (("name" | fixed(10, '_')) :: ("age" | fixed(6, '0').narrow[Int](strToInt, _.toString)))
val personCodec: Codec[Person] = pc.as[Person]
But I do not know how could I use TransformSyntax.as in my case.
What you need is to give a way to tell the compiler that your list of FLEncoder can be converted to a FLEncoder of list. Since we are on the type level, this can be done with a typeclass:
trait ListOfEncoder[L <: HList] {
type Inside <: HList
def merge(l: L): FLEncoder[Inside]
}
object ListOfEncoder {
type Aux[L <: HList, I <: HList] = ListOfEncoder[L] { type Inside = I }
implicit val hnil: Aux[HNil, HNil] = new ListOfEncoder[HNil] {
type Inside = HNil
def merge(l: HNil) = FLEncoder.fixed(_ => "")
}
implicit def hcons[H, T <: HList](implicit T: ListOfEncoder[T]): Aux[FLEncoder[H] :: T, H :: T.Inside] = new ListOfEncoder[FLEncoder[H] :: T] {
type Inside = H :: T.Inside
def merge(l: FLEncoder[H] :: T): FLEncoder[H :: T.Inside] =
FLEncoder.fixed((ht: H :: T.Inside) => l.head.encode(ht.head) + T.merge(l.tail).encode(ht.tail))
}
}
Now, in your `FLEncoder object, add this implicit class:
implicit class EncoderAsGeneric[L <: HList, I <: HList](l: L)(implicit L: ListOfEncoder.Aux[L, I]) {
def as[E](implicit gen: Generic.Aux[E, I]) =
FLEncoder.fixed((e: E) => L.merge(l).encode(gen.to(e))
}
This will allow you to define
implicit val employeeEncoder = (fixed((s: String) => s) ::
fixed((s: String) => s) ::
fixed((s: Int) => s.toString) ::
fixed((s: Boolean) => s.toString) ::
HNil).as[Employee]
And now, all the implicits should be in scope for your Main as it is.
By the way, since you define your FLEncoder with HList, there is no need for ProductTypeClassCompanion anymore, since this is only for inference from base cases.
Given an arbitrary case class Cc( a: String, b: Int ) and a type class trait Tc[T], is there a way to materialize a simple HList (or preferably a labelled record) that contains a type class instance for each parameter of Cc?
def resolveTypeClasses[C] = ???
val resolved: Tc[String] :: Tc[Int] :: HNil = resolveTypeClasses[Cc]
It's hard to make single type parameter function because of need to resolve intermediate Generic.
So from my noob point of view it should be two parameter def, or sequence of two defs, each taking single type parameter. Second variant could be implemented like:
import shapeless._
trait Tc[T]
case class Cc(a: String, b: Int)
trait ResolveTC[L] {
type Out <: HList
val out: Out
}
implicit object ResolveHNilTC extends ResolveTC[HNil] {
type Out = HNil
val out: HNil = HNil
}
implicit def resolveHListTC[X, XS <: HList](implicit tc: Tc[X], rest: ResolveTC[XS]) =
new ResolveTC[X :: XS] {
type Out = Tc[X] :: rest.Out
val out = tc :: rest.out
}
class TCResolver[C] {
def get[L <: HList](implicit gen: Generic.Aux[C, L], resolve: ResolveTC[L]): resolve.Out = resolve.out
}
def resolveTypeClasses[C] = new TCResolver[C]
implicit case object StringInst extends Tc[String]
implicit case object IntInst extends Tc[Int]
with that implementation
val resolved = resolveTypeClasses[Cc].get
will produce the
StringInst :: IntInst :: HNil
I'm trying to make this rather silly example work, planning to then extend it to something more meaningful.
But no luck so far: I get could not find implicit value for parameter ihc
What am I missing?
sealed trait Field[T] { def name: String }
case class IntegerField(name: String) extends Field[Int]
val year = IntegerField("year")
val test = (year :: 23 :: HNil) :: (year :: 2 :: HNil) :: HNil
type TypedMap = IntegerField :: Int :: HNil
def get[L <: HList : <<:[TypedMap]#λ]
(key: IntegerField, list: L)
(implicit ihc: IsHCons.Aux[L, TypedMap, L]
): Option[Int] = {
if( list == HNil ) return None
val elem: TypedMap = list.head
if( elem.head == key ) Some(elem.tail.head)
else get(key, list.tail)
}
get(year, test)
When you write IsHCons.Aux[L, TypedMap, L] you're asking for evidence that the hlist L has head TypedMap and tail L, which would mean that it's an infinite hlist, which isn't possible, since Scala doesn't allow this kind of arbitrarily recursive type (try writing something like type Foo = Int :: Foo, for example—you'll get a "illegal cyclic reference" error). It's also probably not what you want.
In general you're unlikely to use IsHCons much in Shapeless, since it's almost always better just to indicate the structure you want in the type. For example, the following two definitions do the same thing:
import shapeless._, ops.hlist.IsHCons
def foo[L <: HList](l: L)(implicit ev: IsHCons[L]) = ev.head(l)
And:
def foo[H, T <: HList](l: H :: T) = l.head
But the second is obviously preferable (it's clearer, it doesn't require an extra type class instance to be found at compile time, etc.), and it's almost always possible to write whatever you're trying to do that way.
Also note that requiring an IsHCons instance means that the recursion here won't work as stated—you can't call get on an HNil, since the compiler can't prove that it's an HCons (because it's not).
Are you sure you need an hlist at all? If you're requiring that all members of the hlist are of type TypedMap, you might as well use Shapeless's Sized (if you want the type to capture the length) or even just a plain old List.
If you really, really want to use an HList here, I'd suggest writing a new type class:
trait FindField[L <: HList] {
def find(key: IntegerField, l: L): Option[Int]
}
object FindField {
implicit val findFieldHNil: FindField[HNil] = new FindField[HNil] {
def find(key: IntegerField, l: HNil) = None
}
implicit def findFieldHCons[H <: TypedMap, T <: HList](implicit
fft: FindField[T]
): FindField[H :: T] = new FindField[H :: T] {
def find(key: IntegerField, l: H :: T) = if (l.head.head == key)
Some(l.head.tail.head)
else fft.find(key, l.tail)
}
}
def get[L <: HList](key: IntegerField, l: L)(implicit
ffl: FindField[L]
): Option[Int] = ffl.find(key, l)
And then:
scala> get(IntegerField("year"), test)
res3: Option[Int] = Some(23)
scala> get(IntegerField("foo"), test)
res4: Option[Int] = None
This is a pretty common pattern in Shapeless—you inductively describe how to perform an operation on an empty hlist, then on an hlist with a head prepended to a tail that you know how to perform the operation on, etc.
Suppose I have an abitrary KList, which for the sake of argument has type constructor Option[_], ie;
type Example = Option[Int] :: Option[String] :: HNil
Is there a way I can retrieve an Hlist made of the type parameters?
type Params = Int :: String :: HNil
So, for example I might be able to define some sort of arbitrary getOrElse method
getOrElse(ex:Example, default:Params):Params
Now I'm looking for something possibly of a form like this (or similar as the type structure I propose might not be feasible).
case class MyOptionList[L <: HList](maybes:L) {
type Concrete = {somehow the hlist params types as an Hlist}
def getOrElse(default:Concrete):Concrete = ???
}
I'm not Miles, but it's possible to accomplish what you're trying to do pretty elegantly with Shapeless's Comapped:
import shapeless._, ops.hlist.Comapped
case class MyOptionList[L <: HList, C <: HList](maybes: L)(
implicit val comapped: Comapped.Aux[L, Option, C]
) {
def getOrElse(default: C): C = default // Not a useful implementation
}
And then:
scala> val x: Int :: HNil = MyOptionList(Option(1) :: HNil).getOrElse(2 :: HNil)
x: shapeless.::[Int,shapeless.HNil] = 2 :: HNil
Note that in some cases it can be more convenient to put the constraint on the method:
case class MyOptionList[L <: HList](maybes: L) {
def getOrElse[C <: HList: ({ type l[x] = Comapped.Aux[L, Option, x] })#l](
default: C
): C = default
}
Here the usage is the same, but you don't have the extra type parameter on the case class. If you want to use this approach but constrain the creation of MyOptionList to disallow non-Option members, you could use L <: HList: *->*[Option]#λ in its type parameter list.
#MilesSabin will probably show up with a more elegant answer, but you can construct this recursively by hand, the way many of the Shapeless internal things are written:
sealed trait GetOrElse[L <: HList] {
type Concrete <: HList
def getOrElse(ex: L, default: Concrete): Concrete
}
object GetOrElse {
implicit def nil = new GetOrElse[HNil]{
type Concrete = HNil
def getOrElse(ex: HNil, default: HNil) = HNil
}
implicit def cons[H, T <: HList](implicit tl: GetOrElse[T]) =
new GetOrElse[Option[H] :: T]{
type Concrete = H :: tl.Concrete
def getOrElse(ex: Option[H] :: T, default: Concrete) =
ex.head.getOrElse(default.head) ::
tl.getOrElse(ex.tail, default.tail)
}
def apply[L <: HList](implicit goe: GetOrElse[L])
: GetOrElse[L]{type Concrete = goe.Concrete} = goe
}
case class MyOptionList[L <: HList, C](maybes:L)(
implicit goe: GetOrElse[L]{type Concrete = C}) {
def getOrElse(default:C):C = goe.getOrElse(maybes, default)
}
(It's probably possible to use a type member rather than a type parameter, but I get confused about when the type information for those is eaten, so I tend to prefer to use type parameters everywhere)