Scala - avoid too complex nested pattern matching
pattern-matching, scala, slick, spray
Solution
Generally, you can use a for-comprehension to chain together a lot of the monadic structures you have here (including `Try`, `Option`, and `Either`) without nesting. For example:
for {
val1 <- Try("123".toInt)
} yield for {
val2 <- Some(val1).map(_ * 2)
val3 = Some(val2 - 55)
val4 <- val3
} yield val4 * 2
In your style, this might otherwise have looked like:
Try("123".toInt) match {
case Success(val1) => {
val val2 = Some(val1).map(_ * 2)
val2 match {
case Some(val2value) => {
val val3 = Some(val2value - 55)
val3 match {
case Some(val4) => Some(val4)
case None => None
}
}
case None => None
}
case f:Failure => None
}
}
Problem
I try to simplify validation process for serving responses for HTTP requests in Spray (I use Slick for database access). Currently, I check in one query if I should go further to the following query or not (return error). This end up with nested pattern matching. Every validation case can return different error so I can not use any flatMap. ``` class LocationDao { val db = DbProvider.db // Database tables val devices = Devices.devices val locations = Locations.locations val programs = Programs.programs val accessTokens = AccessTokens.accessTokens def loginDevice(deviceSerialNumber: String, login: String, password: String): Either[Error, LocationResponse] = { try { db withSession { implicit session => val deviceRowOption = devices.filter(d => d.serialNumber === deviceSerialNumber).map(d => (d.id, d.currentLocationId.?, d.serialNumber.?)).firstOption deviceRowOption match { case Some(deviceRow) => { val locationRowOption = locations.filter(l => l.id === deviceRow._2.getOrElse(0L) && l.login === login && l.password === password).firstOption locationRowOption match { case Some(locationRow) => { val programRowOption = programs.filter(p => p.id === locationRow.programId).firstOption programRowOption match { case Some(programRow) => { val program = Program(programRow.name, programRow.logo, programRow.moneyLevel, programRow.pointsForLevel, programRow.description, programRow.rules, programRow.dailyCustomerScansLimit) val locationData = LocationData(program) val locationResponse = LocationResponse("access_token", System.currentTimeMillis(), locationData) Right(locationResponse) } case None => Left(ProgramNotExistError) } } case None => Left(IncorrectLoginOrPasswordError) } } case None => Left(DeviceNotExistError) } } } catch { case ex: SQLException => Left(DatabaseError) } } } ``` What is a good way to simplify this? Maybe there is other approach..