Sheng
Sheng

Reputation: 1707

define `Codec` for recursive data structure

I have a class looking like this,

case class Foo ( bar: Int, foos: Vector[Foo] )

to define a Codec[Foo], I tried this,

def fc = shapeless.Lazy((int32 ~~ vector(fc)).widenOpt( (Foo.apply _).tupled, Foo.unapply _ ))

But this did not work, since scodec throws StackOverflowError. What is the right way of doing this ?

Upvotes: 2

Views: 419

Answers (1)

mpilquist
mpilquist

Reputation: 3855

You'll need the scodec.codecs.lazily combinator to build recursive codecs (not shapeless.lazily). For example:

scala> def fc: Codec[Foo] = lazily((int32 :: vector(fc)).as[Foo])
fc: scodec.Codec[Foo]

scala> val res = fc.encode(Foo(1, Vector(Foo(2, Vector.empty)))).require
res: scodec.bits.BitVector = BitVector(64 bits, 0x0000000100000002)

scala> fc.decode(res)
res2: scodec.Attempt[scodec.DecodeResult[Foo]] = Successful(DecodeResult(Foo(1,Vector(Foo(2,Vector()))),BitVector(empty)))

In scodec 1.8.2 and prior, deriving this codec, instead of defining it explicitly, results in an error at compile time, due to the derivation continuing recursively forever. As of 1.8.3, this codec can be automatically derived without issue.

For an example of a recursive tree, see this example from scodec source.

Upvotes: 3

Related Questions