Looks like Lenses topic should not be closed without discussing compose method. This method decreases the count of lenses we have to produce. We called it M * N size issue in previous session.
This time it's hard to not jump into function programming math mess, but we will try.
case class Lens[R, F](get: R => F, set: (R, F) => R) {
def compose[Q](lens2: Lens[Q, R]): Lens[Q, F] =
Lens (get = get compose lens2.get,
set = (q, f) => lens2 set(q, set(lens2 get q, f)))
}
This time it's hard to not jump into function programming math mess, but we will try.
Function1
Indeed you've heard on Function1: class that implements special case for Function. Function of course makes functions are living as first class citizens in Scala. Let's look inside (removing unimportant parts):
trait Function1[ -T1, +R] extends AnyRef { self =>
def apply(v1: T1): R
def compose[A](g: A => T1): A => R = { x => apply(g(x)) }
...
}
Apply method knows how from T1 make R
Lets speak about compose in detail: there a function that knows how A can become T1. While T1 is an input into apply, we can compose A => T1 and T1 => R into A => R. Easy isn't it? We just putting a result from one function as a parameter into apply.
Compose for Lenses
It's time to review our complex:) construction again:
case class Weapon(name: String, ammo: Long)
case class Soldier(rank: Int, weapon: Weapon)
Remember how to implement lens for changing ammo in weapon?
val ammoL = Lens[Weapon, Long](...)
and then changing a weapon of soldier
val weaponL = Lens[Soldier, Weapon](...)
both operates with get method, from weaponL to ammoL:
Function1 [Soldier, Weapon] and Function1 [Weapon, Long]
If we will execute compose : Function1 [Weapon, Long] compose Function1 [Soldier, Weapon] it will return Function1 [Soldier, Long].
Not bad for get function? Coder's intuition should push us to insurance that set can be implemented as well via already defined Lenses. I'm sure U already tired here, just showing a final solution:
def compose[Q](lens2: Lens[Q, R]): Lens[Q, F] =
Lens (get = get compose lens2.get,
set = (q, f) => lens2 set(q, set(lens2 get q, f)))
}
Putting it together and changing soldiers ammo to 0:
val weaponL = Lens[Soldier, Weapon](
get = _.weapon,
set = (s, w) => s.copy(weapon = w)
)
val ammoL = Lens[Weapon, Long](
get = _.ammo,
set = (w, a) => w.copy(ammo = a)
)
val soldierAmmoL = ammoL compose weaponL
soldierAmmoL.set(soldier, 0)
We got Lens, from existed, without implementing it explicitly! Victory!
No comments:
Post a Comment