下面是代码的味道: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]]
它应该获取项目列表,为每个项目获取链接的用户并返回一个新的Item
s列表,或者在任何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-组合器?)。我当然可以切换到命令式实现。我只是很好奇。
在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\
我有scala类,比如A类(b:Seq[字符串]) 我的问题是,当我从没有字段的文本中反序列化它时,我的类包含空字段。可以强制反序列化程序填充空的? 我使用com。fasterxml。杰克逊。数据绑定。带有com的ObjectMapper。fasterxml。杰克逊。单元斯卡拉。默认Scalamodule。 编辑:我想要的解决方案是修复所有此类字段,而不显式地提及它们的完整列表。Ir更改所有声明。
我们有一个流氓生产者将Kafka Header设置为一个类,该类是生产者的一部分,但不是使用Kafka Streams绑定器在Spring Cloud Stream应用程序中实现的消费者的一部分。它导致了一个异常
我使用Clojure 1.6.0,我得到这个错误: 编译异常java。木卫一。FileNotFoundException: 找不到clojure/core/logic\uuu init。类或clojure/core/logic。类路径上的clj:, 编译:(hello\core.clj:8:70)