3
scala> import akka.http.scaladsl.server._; import Directives._
import akka.http.scaladsl.server._
import Directives._

Suppose I have two functions from some type (Int, say) to a Route:

scala> lazy val r1: Int => Route = ???
r1: Int => akka.http.scaladsl.server.Route = <lazy>

scala> lazy val r2: Int => Route = ???
r2: Int => akka.http.scaladsl.server.Route = <lazy>

I can compose a route thusly:

scala> lazy val composite: Route = path("foo"){ r1(1) } ~ path("bar"){ r2(1) }
composite: akka.http.scaladsl.server.Route = <lazy>

What I would like to do is use function composition together with the ~ path chaining. That is, I'd love for this to just work:

scala> lazy val composite: Int => Route = path("foo")(r1) ~ path("bar")(r2)

Except it doesn't :-(

<console>:31: error: type mismatch;
 found   : Int => akka.http.scaladsl.server.Route
    (which expands to)  Int => (akka.http.scaladsl.server.RequestContext => scala.concurrent.Future[akka.http.scaladsl.server.RouteResult])
 required: akka.http.scaladsl.server.Route
    (which expands to)  akka.http.scaladsl.server.RequestContext => scala.concurrent.Future[akka.http.scaladsl.server.RouteResult]
       lazy val composite: Int => Route = path("foo")(r1) ~ path("bar")(r2)
                                                      ^

EDIT

I am trying to do this using point-free function composition,. As Ramon's answer below demonstrates, it's easy to do this if you are willing to duplicate the function application (but this is what I want to avoid). That is:

lazy val composite: Int => Route 
  = i => path("foo")(r1(i)) ~ path("bar")(r2(i))

NOTE

Using scalaz, I can do this:

scala> import akka.http.scaladsl.server._; import Directives._; import scalaz.syntax.monad._; import scalaz.std.function._
import akka.http.scaladsl.server._
import Directives._
import scalaz.syntax.monad._
import scalaz.std.function._

scala> lazy val r1: Int => Route = ???
r1: Int => akka.http.scaladsl.server.Route = <lazy>

scala> lazy val r2: Int => Route = ???
r2: Int => akka.http.scaladsl.server.Route = <lazy>

scala> lazy val composite = for (x <- r1; y <- r2) yield path("foo")(x) ~ path("bar")(y)
composite: Int => akka.http.scaladsl.server.Route = <lazy>

Which is so good as it goes but the implicit ConjunctionMagnet.fromRouteGenerator in akka.http.scaladsl.server gives me cause to think it might be possible in akka-http directly

oxbow_lakes
  • 133,303
  • 56
  • 317
  • 449

1 Answers1

1

The equivalent to the scalaz example you gave is:

lazy val composite: Int => Route = 
  (i) => path("foo")(r1(i)) ~ path("bar")(r2(i))

Similarly, you could anonymize the parameter name but that would result in 2 parameters:

lazy val composite: (Int,Int) => Route = path("foo")(r1(_)) ~ path("bar")(r2(_))
Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
  • I'm not sure I'd say this was equivalent. I guess the point of my question is about point-free function composition, sorry I didn't make that more clear – oxbow_lakes Jul 20 '17 at 12:24
  • @oxbow_lakes Two functions are equal if they map all of the same domain values to the same codomain values. Therefore, my `composite` is equal to your `composite`. Regarding "the point" of your question, this doesn't come "out of the box" because `Function1` doesn't have a `flatMap` method. – Ramón J Romero y Vigil Jul 20 '17 at 12:27
  • That is indeed my "point". I was basically asking if, purely using `akka-http`, I was able to do what I want (i.e. function composition). Not sure how familiar you are with `akka-http` but many things *just work* if you only know the magic incantation of imports. I was suggesting that the implicit in `ConjunctionMagnet` gave me cause to hope that what I wanted might indeed be achievable without having to bring scalaz into the mix – oxbow_lakes Jul 20 '17 at 13:15
  • As for equivalence, I meant in the context of my question, they are not equivalent. – oxbow_lakes Jul 20 '17 at 13:18