2016-07-14 109 views
0

我有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) 
} 

and the the再是一個存儲庫來保存實體

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) 
} 

保存方法被調用鏈中的後,我必須打破進入另一個功能。我的問題是,有沒有辦法將價值從未來中提升出來,然後放回原處,以便我的管道看起來像這樣?

(mapDtoToEntity _ >>> incrementId >>> save ?!? incrementId ?!? mapEntityToDto)(dto) 

哪裏?!?是假設運營商。

它也可能來自scala庫。它不需要來自scalaz。

回答

1

scalaz(或貓)構成返回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,剩下的就是改造從DtoEntity並返回,同時增加id(兩次)。

我們之前和save後通過調用dimap做改造和增量上Kleisli(save)(從Profunctor得到一個操作):

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是curried,所以我們會寫dimap(f)(g)而不是dimap(f, g)
+0

感謝kleisli的真正好解釋。我之前對它有點熟悉,現在對它有了更好的把握。我真的很想知道是否有一位操作員自動執行此操作,但看起來並不像。順便說一句,雙增量是管道中多個操作的一個人爲的例子。我沒有在我的項目中這樣做。 – decapo