Search code examples
scalashapelessscodec

.dropUnit on .hlist'ed codecs doesnt seem to work


For "big" codecs, the Scala phase typer takes forever (we're talking minutes) when creating a codec directly from HLists and applying .dropUnits

( ignore(6) ::
  uint(2) ::
  uint(30) ::
  int(4) ::
  int(8) ::
  uint(10) ::
  bool(1) ::
  int(28) ::
  int(27) ::
  uint(12) ::
  uint(9) ::
  uint(6) ::
  int(2) ::
  ignore(3) ::
  bool(1) ::
  uint(19)
).dropUnits.as[SomeBigCaseClass]

And it seems to be way faster to create a codec with ~, and then applying .hlist like such:

( ignore(6) ~
  uint(2) ~
  ...
).hlist.dropUnits.as[SomeBigCaseClass]

But this doesn't seem work.

Could not prove that this.Out can be converted to/from reports.SomeBigCaseClass.
  ).hlist.dropUnits.as[SomeBigCaseClass]
                      ^

The simplest solution I've found, which is good enough for me is omitting Unit values inline.

( (ignore(6) dropLeft 
   uint(2)) :: 
  ...
).as[SomeBigCaseClass]

For codecs with many ignores, this feature would be highly welcome. What am I doing wrong? Am I totally missing the point of .hlist?


Solution

  • The hlist combinator converts a Codec[A] in to a Codec[A :: HNil]. This is typically used with other combinators, like flatZip, which require an HList based codec.

    Repeated use of the ~ operator creates a left-associated Tuple2 based structure. For example, int8 ~ bool ~ int8 has type Codec[((Int, Boolean), Int)]. The latest scodec snapshot, 1.7.0-SNAPSHOT (though not 1.7.0-RC1), has the flattenLeftPairs method, which converts a left associated tuple codec to an equivalent HList codec.

    A future version of scodec will have a combinator like ~ but rather than nesting Tuple2 instances, it will create TupleN instances. For example: int8 ~~ bool ~~ int8 would have type Codec[(Int, Boolean, Int)]. This has not been integrated yet though. This has both a performance advantage, by reducing the number of tuples allocated, as well as a convenience advantage, because it allows binding to case classes via widenOpt(SomeBigCaseClass.apply, SomeBigCaseClass.unapply).

    Finally, the observed compiler performance problem with dropUnits is indeed a problem with the way that combinator is defined. This has been fixed in scodec-core 1.7.0-SNAPSHOT (not 1.7.0-RC1 though). See https://github.com/scodec/scodec/issues/41.