如何解决如何从功能上处理日志记录副作用
如果一条记录没有相邻的记录,我想登录。有没有纯粹的功能性方法可以做到这一点?可以将副作用与数据转换区分开来的吗?
这是我需要做的一个例子:
val records: Seq[Record] = Seq(record1,record2,...)
val accountsMap: Map[Long,Account] = Map(record1.id -> account1,...)
def withAccount(accountsMap: Map[Long,Account])(r: Record): (Record,Option[Account]) = {
(r,accountsMap.get(r.id))
}
def handleNoAccounts(tuple: (Record,Option[Account]) = {
val (r,a) = tuple
if (a.isEmpty) logger.error(s"no account for ${record.id}")
tuple
}
def toRichAccount(tuple: (Record,a) = tuple
a.map(acct => RichAccount(r,acct))
}
records
.map(withAccount(accountsMap))
.map(handleNoAccounts) // if no account is found,log
.flatMap(toRichAccount)
因此,我认为这种方法存在多个问题,使其无法达到最佳效果。
元组返回类型很笨拙。我必须在后两个函数中都破坏元组。
日志记录功能必须处理日志记录,然后返回没有任何更改的元组。即使没有进行任何转换,也将其传递给.map感觉很奇怪-也许有更好的方法来获得这种副作用。
是否有功能性的方法可以解决此问题?
解决方法
如果您使用的是Scala 2.13或更高版本,则可以使用tapEach,它使用函数A => Unit
在函数的每个元素上施加副作用,然后不变地传递集合:
//you no longer need to return tuple in side-effecting function
def handleNoAccounts(tuple: (Record,Option[Account]): Unit = {
val (r,a) = tuple
if (a.isEmpty) logger.error(s"no account for ${record.id}")
}
records
.map(withAccount(accountsMap))
.tapEach(handleNoAccounts) // if no account is found,log
.flatMap(toRichAccount)
如果您使用的是旧版Scala,则可以提供扩展方法(根据Levi's Ramsey的建议进行了更新):
implicit class SeqOps[A](s: Seq[A]) {
def tapEach(f: A => Unit): Seq[A] = {
s.foreach(f)
s
}
}
,
我可能是错的(我经常是),但是我认为这可以满足所有要求。
records
.flatMap(r =>
accountsMap.get(r.id).fold{
logger.error(s"no account for ${r.id}")
Option.empty[RichAccount]
}{a => Some(RichAccount(r,a))})
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。