2017-07-03 4 views
4

私はこのコードをscalaz7 IOとモナド変圧器を使ってエレガントな純粋な関数形式で書く方法を考え出していますが、頭をかぶっていません。この関数はfindProfileが含まれている -IOとFuture [オプション]モナド変換器

あなたが気づいたよう
val profileT = for { 
    uuid <- OptionT(Future.successful(findUuid(request))) 
    profile <- OptionT(findProfile(uuid)) 
} yield profile 
val profile: Future[Option[Profile]] = profileT.run 

:私は簡単にこのようなOptionT変圧器と不純な関数を書くことができ、このAPIを使用して

def findUuid(request: Request): Option[String] = ??? 
def findProfile(uuid: String): Future[Option[Profile]] = redisClient.get[Profile](uuid) 

はちょうど私がこの単純なAPIを持っている想像します()に副作用があります。私はIOモナドの内部でこの効果を分離し、純粋な機能の外で解釈したいが、すべてを一緒に組み合わせる方法を知らない。

def findProfileIO(uuid: String): IO[Future[Option[Profile]]] = IO(findProfile(uuid)) 

val profileT = for { 
    uuid <- OptionT(Future.successful(findUuid(request))) 
    profile <- OptionT(findProfileIO(uuid)) //??? how to put Option inside of the IO[Future[Option]] 
} yield profile 
val profile = profileT.run //how to run transformer and interpret IO with the unsafePerformIO()??? 

どうすればよいですか?

答えて

3

IOは、同期効果のためのものです。 Taskはあなたがもっと必要なものです! この質問を参照してくださいと答える:What's the difference between Task and IO in Scalaz?

あなたのFutureTaskに変換してから、このようなAPIを持つことができます。

def findUuid(request: Request): Option[String] = ??? 
def findProfile(uuid: String): Task[Option[Profile]] = ??? 

Taskは、同期と非同期の両方の操作を表すことができるので、これは動作しますので、findUuidすることができますまた、 IOの代わりにTaskにラップしてください。

次にあなたがOptionTでこれらをラップすることができます:

val profileT = for { 
    uuid <- OptionT(Task.now(findUuid(request))) 
    profile <- OptionT(findProfileIO(uuid)) 
} yield profile 

そして、どこかで最後にあなたがそれを実行することができます:

profileT.run.attemptRun 

タスクとその逆に先物を変換するため、このリンクをチェックアウト:Scalaz Task <-> Future

+0

のTHX @のルカ・Jacobowitzさんが質問をお持ちの *私は自分のプレイのAction.asyncの内側にこのコードを使用するので、私は返す必要が未来[結果]。タスクをscalaに変換する。将来はタスクの終了を意味する。それが終了するとすぐに、プレイのアクションは同期になります。終了せずにタスクを未来に変換する方法を知っていますか? *さらに進んでいます - IO Monadは延期された計算のみと考えていますか?それがそうであれば、元のfindProfile:Futureもまた怠惰で、ExecutionContextでの計算も延期されていることを意味しますか?この場合、IOをFutureにラップする必要はないかもしれません - この関数はすでに純粋ですか? –

+0

未来の問題は、それが怠惰ではないということです。 Futures本体内で定義した副作用はすべて実行され、デフォルトでは不正な機能になります。詳細についてはこちらをご覧ください:https://www.reddit.com/r/scala/comments/3zofjl/why_is_future_totally_unusable/ 私の提案は、どこからでもタスクを使用して、未来に変換することです: –

+0

ありがとう君は。昨夜、未来/仕事の比較に掘り起こして、このreddit投稿を読んでみてください。 –

1

このコードは、誰か(Play 2.6)にとって役に立ちそうだと思います。

コントローラのメソッドはPureAction ActionBuilder内のコントローラの外部で実行されるため、コントローラのメソッドは純粋な関数です。ルカの答えに感謝します!

まだPlay 2.6のアクション構成の新しいパラダイムに苦しんでいますが、これは別の話です。

FrontendController.scala:

def index = PureAction.pure { request => 
    val profileOpt = (for { 
    uuid <- OptionT(Task.now(request.cookies.get("uuid").map(t => uuidKey(t.value)))) 
    profile <- OptionT(redis.get[Profile](uuid).asTask) 
    } yield profile).run 
    profileOpt.map { profileOpt => 
    Logger.info(profileOpt.map(p => s"User logged in - $p").getOrElse("New user, suggesting login")) 
    Ok(views.html.index(profileOpt)) 
    } 
} 

Actions.scala

端におけるタスク解像度便利なアクション

class PureAction @Inject()(parser: BodyParsers.Default)(implicit ec: ExecutionContext) extends ActionBuilderImpl(parser) { 
    self => 
    def pure(block: Request[AnyContent] => Task[Result]): Action[AnyContent] = composeAction(new Action[AnyContent] { 
    override def parser: BodyParser[AnyContent] = self.parser 
    override def executionContext: ExecutionContext = self.ec 
    override def apply(request: Request[AnyContent]): Future[Result] = { 
     val taskResult = block(request) 
     taskResult.asFuture //End of the world lives here 
    } 
    }) 
} 

コンバータ。Scalaの

Task->未来と未来>タスクの暗黙的な変換器は

implicit class FuturePimped[+T](root: => Future[T]) { 
    import scalaz.Scalaz._ 
    def asTask(implicit ec: ExecutionContext): Task[T] = { 
    Task.async { register => 
     root.onComplete { 
     case Success(v) => register(v.right) 
     case Failure(ex) => register(ex.left) 
     } 
    } 
    } 
} 

implicit class TaskPimped[T](root: => Task[T]) { 
    import scalaz._ 
    val p: Promise[T] = Promise() 
    def asFuture: Future[T] = { 
    root.unsafePerformAsync { 
     case -\/(ex) => p.failure(ex);() 
     case \/-(r) => p.success(r);() 
    } 
    p.future 
    } 
} 
関連する問題