当前位置: 首页 > 知识库问答 >
问题:

Scala,或[\uu,Seq[或[\uu,T]]到或[\uu,Seq[T]]

能正青
2023-03-14

下面是代码的味道:https://scastie.scala-lang.org/bQMGrAKgRoOFaK1lwCy04g

我有两个JSON APIendpoint。首先是项目。cgi以以下格式返回项目对象列表

$ curl http://example.com/items.cgi
[
    ...
    { sn: "KXB1333", ownerId: 3, borrowerId: 0 },
    { sn: "KCB1200", ownerId: 1, borrowerId: 2 },
    ...
]

borerId==0表示项目没有借款人。

其次,用户。cgi,返回查询参数id指定的用户

$ curl http://example.com/user.cgi?id=1
{ id: 1, name: "frank" }

API可能不好,但我必须处理它。现在在Scala中,我想使用这个漂亮的数据模型

case class User(id: Int, name: String)
case class Item(sn: String, owner: User, borrower: Option[User])

我还有以下用于执行HTTP请求的方法

case class ApiFail(reason: String)
def get[T](url: String): Either[ApiFail, T] = ??? /* omitted for brevity */

函数使用一些魔法从URL获取JSON,并从中构造一个JSON(它使用一些库)。IO失败或HTTP状态错误时,它将返回左侧。

我想编写以下函数

def getItems: Either[ApiFail, Seq[Item]]

它应该获取项目列表,为每个项目获取链接的用户并返回一个新的Items列表,或者在任何HTTP请求失败时失败。(对于具有相同ID的用户可能会有冗余请求,但我还不关心备忘录/缓存。)

到目前为止,我只写了这个函数

def getItems: Either[ApiFail, Seq[Either[ApiFail, Item]]]

如果检索某个用户失败,则仅对相应的项而不是整个结果是致命的。下面是实现

def getItems: Either[ApiFail, Seq[Either[ApiFail, Item]]] = {
    case class ItemRaw(sn: String, ownerId: Int, borrowerId: Int)

    get[List[ItemRaw]]("items.cgi").flatMap(itemRawList => Right(
        itemRawList.map(itemRaw => {
            for {
                owner <- get[User](s"users.cgi?id=${itemRaw.ownerId}")
                borrower <-
                    if (itemRaw.borrowerId > 0)
                        get[User](s"users.cgi?id=${itemRaw.borrowerId}").map(Some(_))
                    else
                        Right(None)
            } yield
                Item(itemRaw.sn, owner, borrower)
        })
    ))
}

这看起来像是对家庭作业的要求,但我经常想到我想从一个包装器事物(m-monad?)切换到另一个,我有点困惑于如何仅使用包装器函数(c-组合器?)。我当然可以切换到命令式实现。我只是很好奇。

共有1个答案

李锦
2023-03-14

在FP世界中,有一个词可以准确描述这一点——“遍历”(链接到cats实现)。当您有一个函数和一个函数时使用=

使用CAT,您可以稍微改变您的方法

import cats._, cats.implicits._

def getItems: Either[ApiFail, Seq[Item]] = {
  case class ItemRaw(sn: String, ownerId: Int, borrowerId: Int)

  get[List[ItemRaw]]("items.cgi").flatMap(itemRawList =>
    itemRawList.traverse[({type T[A]=Either[ApiFail, A]})#T, Item](itemRaw => {
      for {
        owner <- get[User](s"users.cgi?id=${itemRaw.ownerId}")
        borrower <-
          if (itemRaw.borrowerId > 0)
            get[User](s"users.cgi?id=${itemRaw.borrowerId}").map(Some(_))
          else
            Right(None)
      } yield
        Item(itemRaw.sn, owner, borrower)
    })
  )
}

话虽如此,我当然可以理解犹豫不决是否要完全走这条路。猫(和斯卡拉兹)是很多需要接受的——尽管我建议你在某个时候这样做!

如果没有它们,您总是可以编写自己的实用程序方法来操作常用容器:

def seqEither2EitherSeq[A, B](s: Seq[Either[A, B]]): Either[A, Seq[B]] = {
  val xs: Seq[Either[A, Seq[B]]] = s.map(_.map(b => Seq(b)))
  xs.reduce{ (e1, e2) => for (x1 <- e1; x2 <- e2) yield x1 ++ x2 }
}

def flattenEither[A, B](e: Either[A, Either[A, B]]): Either[A, B] = e.flatMap(identity)

那么您想要的结果将是:

val result: Either[ApiFail, Seq[Item]] = flattenEither(getItems.map(seqEither2EitherSeq))
 类似资料:
  • 我想写一个调用Java方法的C程序。 我试图从C调用Java函数。如本文所述 http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/invocation.html 但我在调试时遇到这个错误,无法处理它。我正在使用Visual studio 2012。这是我的代码C代码。 安装在my comp版本上的Java是C:\Users\

  • 我们有一个流氓生产者将Kafka Header设置为一个类,该类是生产者的一部分,但不是使用Kafka Streams绑定器在Spring Cloud Stream应用程序中实现的消费者的一部分。它导致了一个异常

  • 我有scala类,比如A类(b:Seq[字符串]) 我的问题是,当我从没有字段的文本中反序列化它时,我的类包含空字段。可以强制反序列化程序填充空的? 我使用com。fasterxml。杰克逊。数据绑定。带有com的ObjectMapper。fasterxml。杰克逊。单元斯卡拉。默认Scalamodule。 编辑:我想要的解决方案是修复所有此类字段,而不显式地提及它们的完整列表。Ir更改所有声明。

  • 我使用Clojure 1.6.0,我得到这个错误: 编译异常java。木卫一。FileNotFoundException: 找不到clojure/core/logic\uuu init。类或clojure/core/logic。类路径上的clj:, 编译:(hello\core.clj:8:70)