Dave Taubler
Dave Taubler

Reputation: 1091

Scala dispatch example

I'm a Java engineer who's been slowly learning Scala recently. I've come across some sample code using Dispatch to make a simple GET request:

val request = url("http://somesite.com")
val result = Http( request OK as.String)

The problem is... I don't quite get what's going on here. First, is Http a class? or a method? Second, what's up with the parameters passed to it? I thought maybe we were passing three arguments, with Scala allowing us to omit commas. But when I tried adding commas, I got a compilation error, so that can't be right.

I'm sure this makes sense to someone fluent in Scala, but I'm not there yet and it's holding me up. I've tried looking for documentation online, but have found nothing helpful.

Upvotes: 12

Views: 4495

Answers (2)

Travis Brown
Travis Brown

Reputation: 139028

Here's a (painfully) explicit version with all the syntactic sugar taken out:

import dispatch.{ Defaults, Http, Req, as, implyRequestHandlerTuple, url }
import scala.concurrent.Future

val request: Req = url.apply("http://somesite.com")

val result: Future[String] =
  Http.apply(
    implyRequestHandlerTuple(request).OK[String](as.String)
  )(Defaults.executor)

url is a singleton object with an apply method that returns an instance of the Req case class. Http is also a singleton object, and it also has an apply method. Http's apply takes two parameter lists—the first taking a single Req parameter, and the second taking an execution context (which you can think of as Scala's version of Java's Executor).

implyRequestHandlerTuple is an implicit method. Req doesn't have an OK method, but the compiler knows that the RequestHandlerTupleBuilder class does (and that it takes the appropriate arguments—in this case a function from Response to some type), so in the original version it automatically applies this method to make the conversion from Req.

Finally, as is a package that contains a String singleton object. This object extends Response => String (which is more syntactic sugar for Function1[Response, String]. Our OK method was looking for a function taking a Response, so we're good there.

In the end you've got a Future[String]. There are plenty of other places to read about futures, so I won't go into detail here, but in short this value can either be unsatisfied (i.e. you're still waiting for a result), or satisfied with a failure (in case of network error, etc.), or satisfied successfully (in which case it'll contain the response body).

Upvotes: 26

Michael Zajac
Michael Zajac

Reputation: 55569

url is (essentially) a method that returns a Req object. So request has type Req.

Http is a class with a companion object that has a few overloads of the apply method. So when you see:

Http(request OK as.String)

It is actually syntactic sugar for:

Http.apply(request OK as.String)

Ok, so what's going on inside apply? It appears as though a method named OK is being called on request. But looking through the API Docs, you may notice there is no such method OK for the type Req. There is, however, a class called RequestHandlerTupleBuilder, which does have such a method. And there is an implicit conversion defined in the dispatch package:

implicit def implyRequestHandlerTuple(builder: Req) =
    new RequestHandlerTupleBuilder(builder)

What's happening here is that when you call request OK, the compiler sees that request does not have an OK method. So it then looks for possible implicit methods that accept Req as a parameter and return types to do have such a method. The above method is the implicit it finds, so the Req is implicitly converted to a RequestHandlerTupleBuilder.

Now let's look at the signature of OK:

def OK [T](f: Response => T): (Request, OkFunctionHandler[T])

It accepts a function as a parameter. In particular, a function that accepts a Response as a parameter, and returns some other type T. In this case, such a function is as.String that has type Response => String. OK will then return a Request tupled with an OkFunctionHandler[T].

This tells me that the overload of apply we're calling is this one:

def apply[T](pair: (Request, AsyncHandler[T])): Future[T]

(OkFunctionHandler extends AsyncHandler)

Looking it all in a slightly more java-like style and with type annotations, you have:

val request: Req = url("http://somesite.com")
val result: Future[String] = Http.apply(request.OK(as.String))

Using only explicit calls, it would look more like:

val result: Future[String] = 
    Http.apply(implyRequestHandlerTuple(request).OK(as.String))

In short, there is only one parameter being passed to Http.apply, it's just using a point-free style to call other methods within.

Upvotes: 9

Related Questions