Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding repetition using lenses whilst deep-copying into Map values

I have an immutable data structure where I have nested values in Maps, like so:

case class TradingDay(syms: Map[String, SymDay] = Map.empty)
case class SymDay(sym: String, traders: Map[String, TraderSymDay] = Map.empty)
case class TraderSymDay(trader: String, sym: String, trades: List[Trade] = Nil)

Separately I have a list of all trades over the day, and I want to generate the TradingDay structure, where

case class Trade(sym: String, trader: String, qty: Int)

I am trying to figure out how I would update this structure with lenses (see appendix) by folding through my trades:

(TradingDay() /: trades) { (trd, d) =>
  def sym = trd.sym
  def trader = trd.trader
  import TradingDay._
  import SymDay._
  import TraderSymDay._
  val mod =
    for {
      _ <- (Syms member sym).mods(
             _ orElse some(SymDay(sym)))
      _ <- (Syms at sym andThen Traders member trader).mods(
             _ orElse some(TraderSymDay(trader, sym)))
      _ <- (Syms at sym andThen (Traders at trader) andThen Trades).mods(
             trd :: _)
      x <- init
    } yield x
  mod ! d
}

This works; but I'm wondering whether I could be less repetitive (in terms of adding to a map and then modifying the value at the key of a map. It doesn't seem that much less annoying than the associated deep-copy.

Appendix - the lenses

object TradingDay {
  val Syms = Lens[TradingDay, Map[String, SymDay]](_.syms, (d, s) => d.copy(syms = s))
}

object SymDay {
  val Traders = Lens[SymDay, Map[String, TraderSymDay]](_.traders, (d, t) => d.copy(traders = t))
}

object TraderSymDay  {
   val Trades = Lens[TraderSymDay, List[Trade]](_.trades, (d, f) => d.copy(trades = f))
}
like image 607
oxbow_lakes Avatar asked Apr 20 '12 12:04

oxbow_lakes


1 Answers

with

type @>[A,B] = Lens[A, B]

and by keeping this lens

val Syms : Lens[TradingDay, Map[String, SymDay]]

and defining those lenses:

val F : Map[String, SymDay] @> Option[SymDay] = ...
val G : Option[SymDay] @> Map[String, TraderSymDay] = ...
val H : Map[String, TraderSymDay] @> Option[TraderSymDay] = ...
val I : Option[TraderSymDay] @> List[Trade] = ...

val J: TradingDay @> List[Trade] = Syms >=> F >=> G >=> H >=> I

you could get this:

(trades /: TradingDay()){ (trd, d) => (J.map(trd :: _).flatMap(_ => init)) ! d }
like image 112
Yo Eight Avatar answered Oct 15 '22 15:10

Yo Eight