Non-breaking error handling was covered in few nice articles and presentations. Was reading one from Tony Morris. Solution for validation is looking little bit overcomplicated for me.
Validation is a good example where non-breaking behaviour can be applied. Thinking on validation framework under application flow with immutable object states - may be this is a great idea for new Validation framework.Lets start from Validation, still duel nature, but Success doesn't has any state and Failure contains the list of errors. Failure is Monadic Zero but it has state and can change the state during "add" operation with help of Semigroup and Context Bound.
We will present errors via List[String]:
Quickly introducing immutable structure with lenses that comes from Scala: Lenses:
Validation is a good example where non-breaking behaviour can be applied. Thinking on validation framework under application flow with immutable object states - may be this is a great idea for new Validation framework.Lets start from Validation, still duel nature, but Success doesn't has any state and Failure contains the list of errors. Failure is Monadic Zero but it has state and can change the state during "add" operation with help of Semigroup and Context Bound.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait Semigroup[A] { | |
def append(a1: A, a2: A): A | |
} | |
sealed abstract class Validation[F: Semigroup] { | |
final def and(other: Validation[F]) = (this, other) match { | |
case (Failure(e1), Failure(e2)) => Failure(implicitly[Semigroup[F]].append(e1, e2)) | |
case (Failure(e1), Success()) => Failure(e1) | |
case (Success(), Failure(e2)) => Failure(e2) | |
case (Success(), Success()) => Success | |
} | |
} | |
final case class Failure[F: Semigroup](e: F) extends Validation[F] | |
final case class Success[F: Semigroup]() extends Validation[F] |
We will present errors via List[String]:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
implicit val listSemigroup = new Semigroup[List[String]] { | |
def append(a1: List[String], a2: List[String]): List[String] = { | |
a1 ::: a2 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class Weapon(name: String, ammo: Long) | |
case class Soldier(rank: Int, weapon: Weapon) | |
case class Lens[R, F](get: R => F, set: (R, F) => R) { | |
def compose[Q](g: Lens[Q, R]): Lens[Q, F] = | |
Lens(get = get compose g.get, | |
set = (q, f) => g set(q, set(g get q, f))) | |
} | |
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 nameL = Lens[Weapon, String]( | |
get = _.name, | |
set = (w, n) => w.copy(name = n) | |
) | |
val soldierAmmoL = ammoL compose weaponL | |
val soldierWeaponName = nameL compose weaponL |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def validateAmmo(ammo: => Long): Validation[List[String]] = { | |
if (ammo > 0) Success[List[String]]() | |
else Failure(List("Negative ammo")) | |
} | |
def validateName(name: => String): Validation[List[String]] = { | |
if (!name.isEmpty) Success[List[String]]() | |
else Failure(List("Empty Name")) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val blaster = new Weapon("", -99) | |
val soldier = new Soldier(4, blaster) | |
val validationR = validateAmmo(soldierAmmoL.get(soldier)) and validateName(soldierWeaponName.get(soldier)) |
No comments:
Post a Comment