Scala类型系统帮助:让一个失败级联到堆栈中



我已经和这头野兽搏斗了几天,需要一些指导。我的原始代码太大太麻烦了,所以我试着在这里创建所有的部分,它(几乎(编译了,我在下面的代码中遇到了一些错误。

我想做的是让一个"失败"级联到我的API层。该域是通过连接到后端来获得按MAC地址进行的设备健康检查列表。一个帐户有多个设备,一个设备有多个mac地址。只有主Mac才会从后端系统获得成功响应。

更新1:这里所说的失败是指后端客户端的连接问题。未知mac(即未找到/解析的mac(不被视为故障。应将其报告为成功。

以下是我迄今为止重新创建的内容,以供您模仿我的系统。线18和19可以切换以查看不同的条件。

import scala.util._
trait EquipmentStatus { val mac: String }
case class Offline(mac: String) extends EquipmentStatus
case class Online(mac: String) extends EquipmentStatus
case class Unknown(mac: String) extends EquipmentStatus
case class EquipmentHealth(mac: String, status: EquipmentStatus)
case class Account(number: Int, equipments: List[Equipment])
case class Equipment(macs: List[String]) { 
def primaryMacs = macs.filter(_.endsWith("00")) 
}
object StatusChecker { 
def checkBatchStatuses(macs: List[String]): 
Try[List[EquipmentStatus]] = 
//Success(macs.map(Online(_))) 
Failure(new Exception("Connection Timed Out"))
}

object DeviceService {
def getMacsByAccount(macs: List[String], equipments: List[Equipment]): Try[List[EquipmentHealth]] = {
for {
mac <- macs 
equipment <- equipments.filter(_.macs.contains(mac))
statuses <- StatusChecker.checkBatchStatuses(equipment.primaryMacs)
} yield resolveStatus(statuses, mac)// ######### HOW DO I CONVERT/COLLECT Try[EquipmentHealth] to Try[List[EquipmentHealth]] AND ALSO ALLOW Try[Exception()] TO PROPAGATE UP AS WELL? 
}
def resolveStatus(statuses: List[EquipmentStatus], mac: String): Try[EquipmentHealth] = {
statuses.partition(_.mac == mac) match {
case (Nil, Nil) => Success(EquipmentHealth(mac, Unknown(mac)))
case (List(one), Nil) => Success(EquipmentHealth(mac, one))
case _ => Success(EquipmentHealth(mac, Unknown(mac)))
}
}
}

val equipments = List(Equipment(List("mac100", "mac222")), Equipment(List("mac333", "mac400")))
val exampleAcc = Account(1234, equipments)
DeviceService.getMacsByAccount(List("mac222"), exampleAcc.equipments)

在我的代码库中,Try实际上是一个自定义的Boxed(非此即彼(类型,包含Successes和Fails。我缺乏理解能力。我想要的是从尝试[装备健康]转到尝试[列出[装备健康]]。

我是不是太复杂了?有没有一种我看不到的更简单的方法?

您不只是想要List[Try[EquipmentStatus]]而不是Try[List[EquipmentStatus]]吗?前者不允许从单个故障中恢复。理解总是产生一个类似迭代的结果,你不能从中返回Try[List[Something]]

为了给出更详细的答案,我需要你澄清预期的行为。奇怪的是,你的DeviceService.resolveStatus只能导致成功,这主要是因为你没有保存任何关于哪些查询失败的信息,所以你无法在"此查找失败">"此MAC地址未知">之间做出决定。我认为未知的情况应该被删除,我们总是认为如果没有返回成功,那就是失败。否则,您需要存储更多的信息,例如List[(String, Try[EquipmentStatus]],其中元组的第一个元素是查询的MAC地址(或者,为了更好的性能,使用一个映射,哪些键是地址(。

getMacsByAccount的当前签名是

def getMacsByAccount(macs: List[String], ...): Try[List[EquipmentHealth]]

这允许单个Success/Failure结果,并且不能独立检查每个mac。如果要跟踪每个mac的错误状态,则需要返回List[Try[EquipmentHealth]]而不是Try[List[EquipmentHealth]]:

def getMacsByAccount(macs: List[String], ...): List[Try[EquipmentHealth]]

实现这一点需要对getMacsByAccount:进行简单的更改

def getMacsByAccount(macs: List[String], equipments: List[Equipment]): List[Try[EquipmentHealth]] =
for {
mac <- macs
equipment <- equipments.filter(_.macs.contains(mac))
statuses = StatusChecker.checkBatchStatuses(equipment.primaryMacs)
} yield
statuses.flatMap(resolveStatus(_, mac))

注意:此时resolveStatus总是返回Success,在这种情况下,它还不如只返回EquipmentHealth。如果对此进行更改,请将上面的flatMap更改为map

评论后编辑

如果您只需要一个失败或成功,那么将整个事件封装在Try中,并使用get:打开内部Try

def getMacsByAccount(macs: List[String], equipments: List[Equipment]): Try[List[EquipmentHealth]] =
Try {
for {
mac <- macs
equipment <- equipments.filter(_.macs.contains(mac))
statuses = StatusChecker.checkBatchStatuses(equipment.primaryMacs).get
} yield resolveStatus(statuses, mac).get
}

如果Try中的任何一个值是Failure,则get将抛出由外部Try捕获的异常。

当然,如果checkBatchStatusesresolveStatus只是在失败时抛出异常,而不是返回Try,那会更简单。

最新更新