Scala中如何更简洁实现基于Cats的registerUser高阶类型方法?
优化Scala Cats实现的用户注册逻辑
我们有如下定义的UserRepository trait,包含判断用户是否注册的isRegistered方法,以及插入新用户的insert方法:
trait UserRepository[F[_]] { def insert(user: domain.User): F[Long] def isRegistered(user: domain.User): F[Boolean] }
核心边界类Boundary的registerUser方法需要实现仅当用户未注册时才创建新用户的逻辑,当前的实现可以通过Cats提供的语法糖进一步简化。
原实现代码
class Boundary[F[_]: Monad] private (userrepo: UserRepository[F]) { def registerUser(user: User): F[Either[UserAlreadyRegistered, Long]] = userrepo.isRegistered(user).flatMap { case true => UserAlreadyRegistered(user).asLeft[Long].pure[F] case false => for { id <- userrepo.insert(user) errorOrId = id.asRight[UserAlreadyRegistered] } yield errorOrId } }
简化方案1:使用ifM语法糖
Cats为F[Boolean]提供了ifM方法,可以直接根据布尔结果分支执行两个F[_]操作,省去模式匹配的冗余代码:
import cats.syntax.all._ class Boundary[F[_]: Monad] private (userrepo: UserRepository[F]) { def registerUser(user: User): F[Either[UserAlreadyRegistered, Long]] = userrepo.isRegistered(user).ifM( ifTrue = UserAlreadyRegistered(user).asLeft[Long].pure[F], ifFalse = userrepo.insert(user).map(_.asRight[UserAlreadyRegistered]) ) }
简化方案2:简化原分支的for推导式
如果不想用ifM,也可以直接把原代码中false分支的for推导式简化为map调用(因为只有一步操作,for推导式在这里是冗余的):
class Boundary[F[_]: Monad] private (userrepo: UserRepository[F]) { def registerUser(user: User): F[Either[UserAlreadyRegistered, Long]] = userrepo.isRegistered(user).flatMap { case true => UserAlreadyRegistered(user).asLeft[Long].pure[F] case false => userrepo.insert(user).map(_.asRight[UserAlreadyRegistered]) } }
这两种方案都基于Cats核心库,既保留了代码的可读性,又大幅减少了冗余的嵌套和模板代码。
内容的提问来源于stack exchange,提问作者Dexter
相关产品推荐
相关产品推荐

