score:3

Accepted answer

you can do this pretty straightforwardly by desugaring a bit:

val writes: writes[foo] = (
  (__ \ "i").write[int] and
  (__ \ "s").write[string] and
  (__ \ "other").write[string]
)(foo => (foo.i, foo.s, "some value."))

the unlift(foo.unapply) is just a fancy way to get a function from a foo to a tuple of the kind required by the preceding applicative builder expression, and you can replace it with your own function that can add whatever you want.

if you really wanted even cleaner syntax, you could use shapeless:

import shapeless.syntax.std.tuple._

val writes: writes[foo] = (
  (__ \ "i").write[int] and
  (__ \ "s").write[string] and
  (__ \ "other").write[string]
)(_ :+ "some value.")

it's beautiful, but may be overkill.

score:1

another option is to use an object builder that implements an unapply that returns the extra values. this makes the writes cleaner, but adds a new object. i have found this useful though as both the apply and unapply method can be used for extra massaging of data into the final object (eg: https://stackoverflow.com/a/22504468/1085606)

example:

case class foo(i: int, s: string)

object foobuilder {
  def unapply(foo: foo): option[(int, string, string)] = {
    some((foo.i, foo.s, "some extra value"))
  }
}

val writes: writes[foo] = ((
  (__ \ "i").write[int] and
    (__ \ "s").write[string] and
    (__ \ "other").write[string]
  )(unlift(foobuilder.unapply))

Related Query

More Query from same tag