12

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.

Dave Taubler
  • 1,081
  • 3
  • 12
  • 25
  • Oh, sorry. It's in Scala for Java Developers, which I'm reading through, but I've also seen similar examples online; e.g. http://stackoverflow.com/questions/21103553/scala-dispatch-simple-get-request, and even on the Dispatch site: http://dispatch.databinder.net/Dispatch.html – Dave Taubler Jan 26 '15 at 02:47

2 Answers2

26

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

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 5
    Maybe you started 12 secs earlier, or maybe you know what words to leave out. Your humanities education dollars at work. I'm going to have my daughter read this answer before she starts on her next book report. I see you use the transition words recommended by Ms Roberts: "finally," "in the end." Check-plus! – som-snytt Jan 26 '15 at 04:42
9

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.

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • Wow, I think I beat you by 12 seconds. I like your answer as well, but you could be clearer about the fact that `request OK as.String` is parsed as `request.OK(as.String)`. – Travis Brown Jan 26 '15 at 03:10
  • @TravisBrown Hard to be clearer than explication at bottom. But is `url` essentially a method? That would be ok if ScalaDoc had a "use case" where you click on a method `url` and it takes you to the object-with-an-apply. I hope the ScalaDoc folks aren't reading this. – som-snytt Jan 26 '15 at 04:38
  • I glazed over `url`, because the OP wasn't really asking about it. Now I have an unofficial measurement of how many of me it takes to make one @TravisBrown. – Michael Zajac Jan 26 '15 at 14:14
  • Have a +1 to even the gap a bit. :) – Travis Brown Jan 26 '15 at 15:05