我有2个数据结构itemDto和itemEntity,它们看起来像这样。
trait ItemDto{
def id: Long
def name: String
}
trait ItemEntity {
def id: Long
def name: String
}
case class Entity(id: Long, name: String) extends ItemEntity
case class Dto(id: Long, name: String) extends ItemDto
我想设置一个看起来像这样的管道
ItemDto => ItemEntity => ItemEntity => Future[ItemEntity] => Future[ItemEntity] => Future[ItemDto]
我也有一些映射器功能
object ItemMapper {
def mapDtoToEntity(dto: ItemDto): ItemEntity = {
Entity(dto.id, dto.name)
}
def mapEntityToDto(entity: ItemEntity): ItemDto = {
Dto(entity.id, entity.name)
}
}
增加实体ID
的函数object ItemEntity{
def incrementId(entity: ItemEntity) = Entity(entity.id + 1, entity.name)
}
并且有一个存储库来保存实体
object ItemRepository {
def save(entity: ItemEntity): Future[ItemEntity] = {
Future{entity}
}
}
最后,我的方法结合了所有这些功能来做这样的事情
import ItemMapper._
import ItemRepository.save
import ItemEntity.incrementId
def addItem(dto: ItemDto) = {
(mapDtoToEntity _ >>> incrementId >>> save >>> {_ map (incrementId _ >>> mapEntityToDto) })(dto)
}
在链中调用save方法后,我必须进入另一个函数。我的问题是,有没有办法将价值提升到未来并重新投入使用,以便我的管道看起来像这样?
(mapDtoToEntity _ >>> incrementId >>> save ?!? incrementId ?!? mapEntityToDto)(dto)
?!?
是假设的运营商。
它也可以来自scala库。它不需要来自scalaz。
答案 0 :(得分:1)
来自scalaz(或者cat)的最有用的概念是组合返回一些F[_]
的函数(如Future
这里)Keisli
箭头。
Kleisli
可以轻松编写多个A => F[B]
函数。这里我们实际上只有一个ItemEntity => Future[ItemEntity]
函数。
我们可以从前3个函数创建Kleisli[Future, ItemDto, ItemEntity]
,然后使用最后两个函数映射它:
import scalaz._, Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
val composition1: Kleisli[Future, ItemDto, ItemDto] =
Kleisli(mapDtoToEntity _ >>> incrementId >>> save)
.map(incrementId _ >>> mapEntityToDto)
您要执行的实际操作是save
,其余的是从Dto
到Entity
的转换,然后在递增ID(两次)时返回。
我们可以通过在save
上调用dimap
(我们从Profunctor
获得的操作)来Kleisli(save)
之前和之后进行转换和增量:
val composition2: Kleisli[Future, ItemDto, ItemDto] =
Kleisli(save).dimap(
mapDtoToEntity _ >>> incrementId,
incrementId _ >>> mapEntityToDto)
或者两次使用dimap
:
val composition3: Kleisli[Future, ItemDto, ItemDto] =
Kleisli(save).dimap(incrementId, incrementId)
.dimap(mapDtoToEntity, mapEntityToDto)
所有这些都给出了相同的结果:
import scala.concurrent.Await
import scala.concurrent.duration._
val futureDto: Future[ItemDto] = composition3(Dto(1L, "test"))
Await.result(futureDto, 1.second)
// ItemDto = Dto(3,test)
对于猫,这看起来或多或少相同:
>>>
运算符,因此需要将其替换为andThen
。dimap
是curry,所以我们会写dimap(f)(g)
而不是dimap(f, g)
。