ActionBuilder 接受 IO[Result] 而不是 Future[Result]

ActionBuilder to accept IO[Result] instead of Future[Result]

我写了一个小例子来在 play 框架中使用来自 scalaz 的 IO monad。下面的示例按预期工作:

object IOAction {
  def apply(action:IO[Result]):Result = action.unsafePerformIO
}

class ExampleController extends Controller {
  val now:IO[DateTime] = IO { DateTime.now }

  def index = Action { request => 
    IOAction { 
      now.map { dateTime => Ok(views.html.index(dateTime)) }
    }
  }
}

但是,我想知道使用 play ActionBuilder 是否会导致稍微不那么冗长 API。理想情况下,我想写:

class ExampleController extends Controller {
  val now:IO[DateTime] = IO { DateTime.now }

  def index = IOAction { request => 
    now.map { dateTime => Ok(views.html.index(dateTime)) }
  }
}

我卡住了,因为 invokeBlock 函数似乎固定为 Future[Result] 类型。

def invokeBlock[A](request: R[A], block: P[A] => Future[Result]): Future[Result]

有谁知道 IOAction 的行为与 Action 相同的解决方法(即可选的请求参数和正文解析器)?

如果你重新定义 IOAction:

后一个例子不应该工作吗
object IOAction {
  def apply(io: Request[AnyContent] => IO[Result]): Action[AnyContent] =
    Action { (request: Request[AnyContent]) => io(request).unsafePerformIO }
}

Controller 对象中你可以做

def foo(): Handler = IOAction { request =>
  Ok("foo").point[IO]
}

Future 是异步 (IO) 计算,其中 IO 是同步的。从IOFuture的转换就这么简单。


IOActionBuilder 的组合并没有真正锻炼。 ActionBuilder 并不是在考虑 IO 的情况下制作的。它有

final def apply(block: ⇒ Result): Action[AnyContent]
final def apply(block: (R[AnyContent]) ⇒ Result): Action[AnyContent]
final def async(block: ⇒ Future[Result]): Action[AnyContent]
final def async(block: R[AnyContent]) ⇒ Future[Result]): Action[AnyContent]

你可以使特征扩展 ActionBuilder[R] 并提供,比如说:

final def io(block: => Result): Action[AnyContent]
final def io(block R[AnyContent] => Result): Action[AnyContent]

那么你可以定义:

object IOAction extends IOActionBuilder[Request] { /* ... */ }

并将其用作:

 def foo = IOAction.io { request => 
   Ok("foo").point[IO]
 }

这与我们最初定义的几乎完全相同,但不是那么直接。


ActionComposition.scala兴趣在 组合动作,不提供 基本动作 .