As I am still addicted to the last.fm API and I wanted to create somthing useful while learning a new programming language, I decided to start coding a brand-new all-shiny last.fm API library for scala, because nothing sufficient exists in this area. From my point of view, the best scala approach until now was made by the Guardian.
So, lets start by making a simple sample request:
class Artist(configuration:Configuration) {
val apiService = configuration.apiService;
def getInfo(name: String): Either[ArtistProfile, Error] = {
val call = new Call("artist", "getInfo", Map("artist" -> name))
val response = apiService.service(call)
response.result(ArtistProfile.apply)
}
}
The code looks fairly readable, doesn't it? We have a class which takes a parameter of type Configuration and a method which returns either an ArtistProfile or an Error.
Lets dig a bit into that:
The first line of getInfo creates a new call object, which stores e.g. the method to call and the methods parameters, so no magic here.
In the second line, we use the apiService field to receive the response via the service method. Nothing special is going on until now. As you can see, no type is specified for the response variable. This is one handy aspect of scala called type inference. In many cases, the Scala compiler can automatically assume the type of the variable. The val statement makes the variable immutable(say: final in Java), in contrary to a var statement. It should be sayd that you also can specify the type if you want to.
Now the response variable should contain on Object of type Response.
Lets take a look inside that class:
class Response(val xml: NodeSeq) {
/**
* Gets the root element of the xml inside the <lfm> tag
*/
val dataXml = xml(0).child.collect {case e : Elem => e}
/**
* Return the Error or None
*/
val error: Option[Error] = Error(xml)
def result[T]( xmlConverter :(NodeSeq) => T) : Either[T, Error] =
error.toRight(xmlConverter(xml))
}
The constructor takes an xml. In scala, all constructor parameters are stored as class fields. Again, the xml field is made immutable so noone can change it.
I don't want to go much into the details of dataXml, only that it contains the xml of the API response. The error field is far more interesting:
Error(xml) produces something of a type Option which takes Error as the type of the generic. Generics are typed between square brackets in Scala, so it is like in Java.
The option type represents either Some(Object) or None. Both Some and None are of type Option, of course. So Option[Error] means, either there is Some(Error) or None.
Now to the result method:
As you can see, it returns an Either[T, Error]. Either can be used to represent a kind of type choice. Either the API request was successful and result returns the resulting object of type T or an error occured, in which case, we return an Error object. Either has two subtypes Left and Right, which represent the left, respective the right generic of Either.
The Option type is a bit similar to Either and conveniently has two functions toLeft and toRight. You can guess that they accordingly return a Left or Right. In the example above, toRight returns an Error if the value of the error field is Some(Error).
In the None case, it returns an object of type T.
Wait! What is xmlConverter?
The answer is: A function. Scala is not only object-oriented, but also functional. If we want to parse the xml to a result object of type T, this could become a bit tedious in Java, but not in Scala.
We simply can use a function as argument, which converts the result's xml into an shiny object.
The variable name for this function argument is xmlConverter. It takes, as mentioned before, a NodeSeq (a set of xml nodes) and returns something of type T. We don't need to take care how it does that, only that we can use it.
Long story short, xmlConverter(xml) takes some xml and returns an object of type T. The created object now is a parameter for the toRight method of the Option[Error]. If there wasn't an error, our converted object is returned and else an error.
In Java, you would create an interface(say: Lots of different interface implementations) which you then can use as a function arument, but It's not nearly as powerful as functions as argument of other functions.
At first glance it seems like magic, but if you have tried some Haskell before, it's nothing unusual anymore.
So now we have a generic result-XML to resulting object method.
Back to the artist class:
Here we can see that the function we use here is called apply and belongs to the ArtistProfile class. It should be said that in this case, apply is static and serves as a factory method. The factory pattern is very common in Scala.
Hopefully you have now learned how beautiful an innocent-looking piece of code can be and how powerful Scala as a functional, object-oriented language is. Comparing to Java, Scala is much less verbous: Optionan braces and semi-colons, forced indentation for beautiful code and so many more genious things which aren't available in Scala.
It should be said that you can use any of your old Java code within Scala, as it runs inside the Java Virtual Machine.
I don't want to go much into the details of dataXml, only that it contains the xml of the API response. The error field is far more interesting:
Error(xml) produces something of a type Option which takes Error as the type of the generic. Generics are typed between square brackets in Scala, so it is like in Java.
The option type represents either Some(Object) or None. Both Some and None are of type Option, of course. So Option[Error] means, either there is Some(Error) or None.
Now to the result method:
As you can see, it returns an Either[T, Error]. Either can be used to represent a kind of type choice. Either the API request was successful and result returns the resulting object of type T or an error occured, in which case, we return an Error object. Either has two subtypes Left and Right, which represent the left, respective the right generic of Either.
The Option type is a bit similar to Either and conveniently has two functions toLeft and toRight. You can guess that they accordingly return a Left or Right. In the example above, toRight returns an Error if the value of the error field is Some(Error).
In the None case, it returns an object of type T.
Wait! What is xmlConverter?
The answer is: A function. Scala is not only object-oriented, but also functional. If we want to parse the xml to a result object of type T, this could become a bit tedious in Java, but not in Scala.
We simply can use a function as argument, which converts the result's xml into an shiny object.
The variable name for this function argument is xmlConverter. It takes, as mentioned before, a NodeSeq (a set of xml nodes) and returns something of type T. We don't need to take care how it does that, only that we can use it.
Long story short, xmlConverter(xml) takes some xml and returns an object of type T. The created object now is a parameter for the toRight method of the Option[Error]. If there wasn't an error, our converted object is returned and else an error.
In Java, you would create an interface(say: Lots of different interface implementations) which you then can use as a function arument, but It's not nearly as powerful as functions as argument of other functions.
At first glance it seems like magic, but if you have tried some Haskell before, it's nothing unusual anymore.
So now we have a generic result-XML to resulting object method.
Back to the artist class:
response.result(ArtistProfile.apply)
Here we can see that the function we use here is called apply and belongs to the ArtistProfile class. It should be said that in this case, apply is static and serves as a factory method. The factory pattern is very common in Scala.
Hopefully you have now learned how beautiful an innocent-looking piece of code can be and how powerful Scala as a functional, object-oriented language is. Comparing to Java, Scala is much less verbous: Optionan braces and semi-colons, forced indentation for beautiful code and so many more genious things which aren't available in Scala.
It should be said that you can use any of your old Java code within Scala, as it runs inside the Java Virtual Machine.