for之后如何避免平面图

How to avoid the flatmap after for

我有以下代码片段:

(for {
  _ <- LiveUserQuery.make(DbManager.failRollback).create(user)
  - <- IO.sleep(2.seconds)
  a <- router.run(Request(GET, uri"/user/d85ec250-bb5c-11ea-b3de-0242ac130030")).value
} yield a).flatMap {
  case Some(req) =>
    req.as[User].map { u =>
      val is_uuid_valid = u.id.compareTo(UUID.fromString("d85ec250-bb5c-11ea-b3de-0242ac130030")) == 0
      expect(is_uuid_valid) && expect(u.gender == "F")
    }
  case None => expect(false)
}

并想避免 for 之后的 flatMap。如何将代码块从 flatMap 移动到 for?

您可以使用 match 表达式:

for {
  _ <- LiveUserQuery.make(DbManager.failRollback).create(user)
  - <- IO.sleep(2.seconds)
  a <- router.run(Request(GET, uri"/user/d85ec250-bb5c-11ea-b3de-0242ac130030")).value
  result <- a match {
    case Some(req) =>
      req.as[User].map { u =>
        val is_uuid_valid = u.id.compareTo(UUID.fromString("d85ec250-bb5c-11ea-b3de-0242ac130030")) == 0
        expect(is_uuid_valid) && expect(u.gender == "F")
      }
    case None => expect(false)
  }
} yield result

类似于 Matthias,但使用 fold 代替。

for {
  _ <- LiveUserQuery.make(DbManager.failRollback).create(user)
  - <- IO.sleep(2.seconds)
  a <- router.run(Request(GET, uri"/user/d85ec250-bb5c-11ea-b3de-0242ac130030")).value
  result <- a.fold(ifEmpty = expect(false)) { req =>
    req.as[User].map { u =>
      val is_uuid_valid = u.id.compareTo(UUID.fromString("d85ec250-bb5c-11ea-b3de-0242ac130030")) == 0
      expect(is_uuid_valid) && expect(u.gender == "F")
    }
  }
} yield result

尝试OptionT

(for {
  _ <- OptionT.liftF(LiveUserQuery.make(DbManager.failRollback).create(user))
  _ <- OptionT.liftF(IO.sleep(2.seconds))
  a <- OptionT(router.run(Request(GET, uri"/user/d85ec250-bb5c-11ea-b3de-0242ac130030")).value)
  u <- OptionT.liftF(a.as[User])
  is_uuid_valid = u.id.compareTo(UUID.fromString("d85ec250-bb5c-11ea-b3de-0242ac130030")) == 0
  res <- OptionT.liftF(expect(is_uuid_valid) && expect(u.gender == "F"))
} yield res).getOrElseF(expect(false))

另一种方法,使用额外的 for/yield:

for {
  _ <- LiveUserQuery.make(DbManager.failRollback).create(user)
  _ <- IO.sleep(2.seconds)
  a <- router.run(Request(GET, uri"/user/d85ec250-bb5c-11ea-b3de-0242ac130030")).value
  res = (for {
    req <- a
    u <- req.as[User]
    is_uuid_valid = u.id.compareTo(UUID.fromString("d85ec250-bb5c-11ea-b3de-0242ac130030")) == 0
  } yield (expect(is_uuid_valid) && expect(u.gender == "F"))).getOrElse(expect(false))
} yield res