Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shapeless: batch update using Record

I have a need for modifying multiple fields of a case class without all the copy code. Seems shapeless is a good way to go.

According to example, I could use lens in this form:

lensA ~ lensB ~ lensC set(something)(valA, valB, valC)

which is good. However in my case, nested fields are not my biggest concern (I'm sure it will :-< ). So the lens solution is pretty much the same as:

something.copy(a = valA, b = valB, c = valC)

One thing I'd like to point out is that, not all the modifications are necessarily happened. In my pseudo case, I may update all a,b,c or some of them, or none, based on some if/elses within the context.

Therefore, Record with this kind of usage is pretty much what I need:

someHList + ('a ->> valA) + ('b ->> valB) + ('c ->> valC)

Even ultimately:

Seq(
  'a ->> valA, 
  'b ->> valB,
  'c ->> valC
).fold(someHList)(_ + _)

which is not possible according to my compiler (yield type mismatch error).

I know this usage only exists in my imagination, not documentation. However I'd really appreciate the correct way of using Record or lens or whatever to solve my problem. Any other elegant way is also welcomed!

THX!

like image 646
noru Avatar asked Mar 25 '26 09:03

noru


1 Answers

There is already update single field operation + via the Updater operation provider and only thing you need is to apply it via some fold operation

So you can write

import shapeless._
import shapeless.ops.hlist.LeftFolder
import shapeless.ops.record.Updater
import syntax.singleton._
import record._

object updateAll extends Poly2 {
  implicit def updateOne[L <: HList, F](implicit update: Updater[L, F]) = at[L, F]((l, f) => update(l, f))
}

implicit class UpdateAllOps[L <: HList](record: L) {
  def ++>[U <: HList](updates: U)(implicit fl: LeftFolder[U, L, updateAll.type]): fl.Out =
    fl(updates, record)
}

now having

val rec = 'x ->> "Old" :: 'y ->> 1 :: HNil
val upd = 'z ->> true :: 'x ->> "New" :: HNil

You can verify that

rec ++> upd

Is same that

'x ->> "New" :: 'y ->> 1 :: 'z ->> true :: HNil

But important note that

val str = "New".asInstanceOf[String with Serializable]
rec ++> ('x ->> str :: HNil)

will result in something like

'x ->> "Old" :: y ->> 1 :: 'x -> "New" :: HNil

so you should be very careful with your types here, unless you define your own replacement for Updater

like image 55
Odomontois Avatar answered Mar 27 '26 21:03

Odomontois



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!