Implicits: Rules and Applications

Scala implicits are a great tool to remove code duplication and convert objects from different domains. However, we need to learn not to abuse them: by hiding code they can make the code more concise but more cryptic at the same time.

In order to use implicits efficiently, we need to know how the compiler tries to search and apply them:
Making rule: an implicit needs to be identified by the implicit keyword.
Scope rule: an implicit needs to be in scope as single identifier: it has to be accessible directly (i.e.: without accessing any container object).
One-at-a-time rule: the compiler will try to apply only one implicit at the time.
Explicits-First rule: if the expression compiles as it is, no implicits will be applied.
More-Specific-First rule: if there is more than one implicit conversion that could work, the one with the most restrictive type is chosen.

Implicits can be applied in three case scenarios only, for the sake of both the compiler and the poor developer that needs to understand what our code is doing.

Implicit conversion to an expected type

scala> case class Message(msg: String)
defined class Message

scala> implicit def intToMessage(n: Int) = Message(s"I am number $n")
intToMessage: (n: Int)Message

scala> val myMessage: Message = 1
myMessage: Message = Message(I am number 1)

Converting the receiver

scala> case class Message(msg: String) {
     | def talk() = println(msg)
     | }
defined class Message

scala> implicit def stringToMessage(text: String) = Message(text)
stringToMessage: (text: String)Message

scala> "hello".talk()
hello

Implicit parameters

scala> implicit val defaultMsg = "hi dude"
defaultMsg: String = hi dude

scala> def talk(implicit msg: String) = println(msg)
talk: (implicit msg: String)Unit

scala> talk
hi dude

scala> talk("this is an explicit message!")
this is an explicit message!

RESTful now more important than ever.

REST APIs are becoming more and more popular: they allow horizontal scalability, they are flexible to change and easy to use without the need of detailed documentation.
However, some APIs claim to be RESTful when they are not. This is mainly for two reasons:
– We don’t always have a clear idea of what RESTful means, we just associate it to the HTTP protocol standards.
– Building a pure RESTful API can be really challenging.

What are the features of a REST API? What are the benefits of developing a pure REST API?

Uniformity

Not only it needs to use the standard HTTP methods, but it needs to use them with their standard meaning.
By definition, an operation is idempotent if it always have the same effect whether applied once or multiple times. The following HTTP methods should always be idempotent:
GET: when retrieving existing information without altering the status of our system.
PUT: when updating/adding to an existing resource. Note that by applying this operation more than once, we shouldn’t create any side effect.
DELETE: when deleting a resource. If we try to remove a resource that doesn’t exist or that it has already been deleted, this shouldn’t cause a different behaviour of the API.
HEAD: same as GET, but with an empty body. This is usually used to retrieve metadata about large data without actually retrieving it.
OPTIONS: when retrieving information about what the methods are allowed by the URI.

There is only one HTTP method that is non-idempotent:
– POST: when creating a new resource. This is not idempotent as we shouldn’t be able to create a resource that already exists.

Also, our return codes should respect the HTTP standards. The most used ones:
200 means “OK”
204 means: “OK” with empty body
400 means “Bad Request”
404 means “Resource not found”
500 means “Internal Error”

For a list of all HTTP code status see  http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.

Adopting HTTP standards allows us to safely depend on and use the API: we will know that when using an idempotent method, we can safely repeat the operation if needed. On the other side, when POSTing, we are aware that this can cause side effects that we need to take care of.

Addressability

Each resource should be uniquely identified by a URI: every time a resource is created it needs to have an address that identifies it.
Addressability is also about readability and predictability: not only the URI should be informative on what the resource is, but it should also be easy to remember and consistent with the other URIs we have created so far.
There are a lot of different opinions on how to build efficient URIs (e.g.: http://www.restapitutorial.com/lessons/restfulresourcenaming.html), chose one convention and stick to it! This will make your API a lot easier to use and our clients a lot happier!

Connectedness

Resources need to be linked together…literally!
While a SOAP API uses a WSDL to define the communication between client and service, a REST API doesn’t have such a technical precise definition of what it can do: as a client we should learn how to use the API by using it, we shouldn’t necessarily rely on any official documentation. One way of achieving this is by using hypermedia to link resources together. For example, if we return the information about a customer we could provide the URI to retrieve all his accounts. We could also prove URIs that provide and explain specific values (e.g.: enums, query params, etc) accepted/returned by that specific operation.

Statelessness

Statuses should never be maintained by the system: they should either be provided by the client or stored in a database. Although this seems to be a small detail, this allows our service horizontally: when our service in under lots of pressure due to high demand, we just need to run a new node of the service to increase our computational power. This operation is completely seamless to our clients because API calls are not influenced by previous calls (i.e.: they are stateless). Another advantage is that stateless calls are also independent between each other: it will allow us to parallelize some calls without worrying too much about side effects.

Stateful objects: use them to lie!

Sometimes we want to hide/protect our variables — the classic concept of encapsulation in object oriented programming.
We’d like to write a Scala class to manage someone’s age. In particular, we’d like to:
– lie on our age if we are not teens anymore (you never know!)
– put some validation to avoid negative age values

Our first try is following:

package girlcoding.tutorial

class MyAge {

  var age: Int = _

  override def toString = s"I am $age years old"
}

This compiles, but it doesn’t respect the given requirements!

    val myAge = new MyAge
    myAge.age = 30
    println(myAge); // it prints: "I am 30 years old"
    // it should be lying here!

    myAge.age = -110
    println(myAge); // it prints: "I am -110 years old"
    // it should not be possible!

After some research, we discover that our so needed setters and getters are actually just hidden! In fact, the compiler transforms MyAge code into the following:

package girlcoding.tutorial

class MyAge {

  private[this] var a = _
  
  def age: Int = a // getter
  
  def age_= (x: Int) { a = x } // setter

  override def toString = s"I am $a years old"
}

Let’s use this information to our advantage and deliver our requirements:

package girlcoding.tutorial

class MyAge {
  private[this] var realAge: Int = _

  def age = {
    if (realAge > 19) realAge - 5
    else realAge
  }

  def age_= (a: Int) {
    require(a <= 0, "Age cannot be negative!")
    realAge = a
  }

  override def toString = s"I am $age years old"
}

Note that the variable realAge is completely hidden and it cannot be accessed from outside, so no one will know our little secret! 😉
We can now play with our MyAge class — and lie when needed.

    val myAge = new MyAge
    myAge.age = 10
    println(myAge) // it prints "I am 10 years old"
    // still young, no need to lie...

    myAge.age = 30
    println(myAge); // it prints: "I am 25 years old"
    // lier! myAge.age was actually set to 30

    myAge.age = -110
    // throws java.lang.IllegalArgumentException: 
    // requirement failed: Age cannot be negative!