我有一个构建在Play / Lagom堆栈之上的应用程序。我需要调用一个需要Source[T, NotUsed]
才能将文件流式传输到它的服务。这是服务的接口:
def foo(fooId: UUID): ServiceCall[Source[ByteString, NotUsed], Source[String, NotUsed]]
因此,我通过以下方式使用Play文档中的Accumulator.source
:
private def doSomething(fooId: UUID): BodyParser[Future[Seq[String]]] = BodyParser { _ =>
Accumulator.source[ByteString]
.mapFuture { source: Source[ByteString, NotUsed] =>
externalService
.foo(fooId)
.invoke(source)
.map { x =>
Right(x.runWith(Sink.seq[String]))
}
}
}
现在,在mapFuture
调用中,source
的类型为Source[ByteString, _]
,但是如果我将其更改为Source[ByteString, NotUsed]
以便调用服务,如上面的示例,我在IDE中遇到了一个错误
应该是Source[ByteString, _]
。 _
是否应该意味着我可以将类型更改为想要的任何类型,包括NotUsed
?另一方面,这两者在语义上是否应该等效吗?我发现,当在某些以前的版本中物化值无关紧要时,Akka团队引入了akka.NotUsed
来代替Unit
,但这仍然没有为我提供解决问题的线索。
答案 0 :(得分:7)
另一方面,这两者在语义上不应该等效吗? 反正?
不。它们不是等效的,因为一个是另一个的子类型,反之亦然。
Source[ByteString, NotUsed]
是Source[ByteString, _]
的子类型。并且由于Source
是协变Source[ByteString, _]
与Source[ByteString, Any]
相同。
implicitly[Source[ByteString, NotUsed] <:< Source[ByteString, _]]
implicitly[Source[ByteString, _] =:= Source[ByteString, Any]]
implicitly[Source[ByteString, Any] =:= Source[ByteString, _]]
Accumulator.source[ByteString]
has type Accumulator[ByteString, Source[ByteString, _]]
。 .mapFuture(..)
accepts Source[ByteString, _] => Future[B]
。并且由于X => Y
在Y
中是协变的,但在X
中是协变的,因此Source[ByteString, _] => Future[B]
是Source[ByteString, NotUsed] => Future[B])
的子类型
implicitly[(Source[ByteString, _] => Future[B]) <:< (Source[ByteString, NotUsed] => Future[B])]
因此,您可以使用Source[ByteString, _] => Future[B]
代替Source[ByteString, NotUsed] => Future[B]
,反之亦然。
({B
似乎是Future[Either[Result, Future[Seq[String]]]]
。)