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
FLEncodercan be converted to aFLEncoderof list. Since we are on the type level, this can be done with a typeclass:Now, in your `FLEncoder object, add this implicit class:
This will allow you to define
And now, all the implicits should be in scope for your
Mainas it is.By the way, since you define your FLEncoder with
HList, there is no need forProductTypeClassCompanionanymore, since this is only for inference from base cases.