0

I wrote a small example to use the IO monad from scalaz within the play framework. The example below works as expected:

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

However, I'm wondering if the use of a play ActionBuilder might result in a slightly less verbose API. Ideally, I would like to write:

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

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

I got stuck because the invokeBlock function seems to be fixed to the Future[Result] type.

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

Does anyone know a workaround where IOAction behaves just the same as Action (ie. an optional request parameter and body parser)?

user3459840
  • 125
  • 8

1 Answers1

2

Shouldn't the latter example work, if you redefine IOAction:

object IOAction {
  def apply(io: Request[AnyContent] => IO[Result]): Action[AnyContent] =
    Action { (request: Request[AnyContent]) => io(request).unsafePerformIO }
}

And in Controller object you could do

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

I.e. Future is asynchronous (IO) computation, where IO is synchronous. Transformation from IO to Future is thus easy.


The combination of IO and ActionBuilder doesn't really workout. ActionBuilder is not made with IO in mind. It has

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]

You can make a trait extending ActionBuilder[R] and providing, say:

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

Then you could define:

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

And use it as:

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

This is almost exactly the same what we defined first, not so directly though.


In ActionComposition.scala interest is in composing actions, not providing the base action.

phadej
  • 11,947
  • 41
  • 78
  • The first solution doesn't work if you want to access the request parameter as well, the second solution doesn't work either because to use the IOAction in combination with the ActionBuilder the `io` parameter of IOAction should be of type `Action[A]` I guess. See: https://github.com/mariussoutier/PlayBasics/blob/master/play-2.2-migration/app/controllers/ActionComposition.scala – user3459840 Jan 27 '15 at 15:01