Skip to content

Instantly share code, notes, and snippets.

@jrwest
Created January 18, 2012 03:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrwest/1630635 to your computer and use it in GitHub Desktop.
Save jrwest/1630635 to your computer and use it in GitHub Desktop.
Scalaz Lenses Example
import scalaz._
import Scalaz._
case class ConditionalMapLens[A, K, V](lens: Lens[A, Map[K,V]]) {
def +=?(elem: (K, Option[V])) = lens.mods(m => elem._2.cata(v => m + (elem._1 -> v), m))
def +=?(elem: (K, V), bool: Boolean) = lens.mods(m => if (bool) m + elem else m)
def -=?(k: K, bool: Boolean) = lens.mods(m => if (bool) (m - k) else m)
}
sealed trait ToyCondition
case object New extends ToyCondition
case object Used extends ToyCondition
case object Destroyed extends ToyCondition
case class Toy(condition: ToyCondition, daysOld: Int)
case class ToyBox(toys: Map[String, Toy])
sealed trait ToyBoxUpdate
case class BoughtToy(toy: Toy) extends ToyBoxUpdate // bought dog new toy
case class TrashedToy(toy: Toy) extends ToyBoxUpdate // threw toy in garbage
case class ModifiedToy(toy: Toy) extends ToyBoxUpdate // dog changed state of toy (new -> used, etc)
case class ToyBoxUpdates(report: Map[String, ToyBoxUpdate])
object ToyBoxExample extends App {
implicit def LensToConditionalMapLens[A, K, V](lens: Lens[A, Map[K, V]]): ConditionalMapLens[A, K, V] = ConditionalMapLens(lens)
val exampleToyBox = ToyBox(Map("ball" -> Toy(New, 1), "rope" -> Toy(Used, 3)))
val exampleUpdates = ToyBoxUpdates(Map(
"ball" -> ModifiedToy(Toy(Used, 2)),
"frisbee" -> BoughtToy(Toy(New, 0)),
"rope" -> TrashedToy(Toy(Destroyed, 4)),
"missing" -> ModifiedToy(Toy(New, 0)),
"badremove" -> TrashedToy(Toy(Destroyed, 2))
))
val toyBox: Lens[(ToyBoxUpdates, ToyBox), Map[String, Toy]] =
Lens.snd[ToyBoxUpdates, ToyBox] andThen Lens(_.toys, (b,ts) => b copy (toys = ts))
val toyBoxUpdates: Lens[(ToyBoxUpdates, ToyBox), Map[String, ToyBoxUpdate]] =
Lens.fst[ToyBoxUpdates, ToyBox] andThen Lens(_.report, (u, r) => u copy (report = r))
def applyUpdates(updates: ToyBoxUpdates, toybox: ToyBox): (ToyBoxUpdates, ToyBox) =
updates.report.foldLeft((updates, toybox)) { (res, info) =>
(applyUpdate(res)(_: String, _: ToyBoxUpdate)).tupled(info)
}
def applyUpdate(result: (ToyBoxUpdates, ToyBox))
(toyName: String, update: ToyBoxUpdate): (ToyBoxUpdates, ToyBox) = {
update match {
case BoughtToy(toy) =>
handleAddedUpdate(toyName, toy) ~> result
case TrashedToy(_) =>
handleRemovedUpdate(toyName) ~> result
case ModifiedToy(toy) =>
handleModifiedUpdate(toyName, toy) ~> result
}
}
def updateToyBox(toyName: String, toy: Toy) =
for {
mbOriginal <- toyBox member toyName
_ <- toyBox += ((toyName, toy))
} yield mbOriginal
def handleAddedUpdate(toyName: String, toy: Toy) =
for {
mbToy <- updateToyBox(toyName, toy)
_ <- toyBoxUpdates +=? ((toyName, mbToy.map(ModifiedToy(_))))
} yield ()
def handleRemovedUpdate(toyName: String) =
for {
mbToy <- toyBox member toyName
_ <- toyBox -= toyName
_ <- toyBoxUpdates -=? (toyName, mbToy >| false | true) // mbToy >\ false | true is short for mbToy.map(_ => false).getOrElse(true)
} yield ()
def handleModifiedUpdate(toyName: String, toy: Toy) =
for {
mbToy <- updateToyBox(toyName, toy)
_ <- toyBoxUpdates +=? (toyName -> BoughtToy(toy), !mbToy.isDefined)
} yield ()
println(applyUpdates(exampleUpdates, exampleToyBox))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment