Scala in practice



Scala in practice

0 0


scala-in-practice

A talk done in Kaunas Java User Group, 2016-04-20

On Github donatasm / scala-in-practice

Scala in practice

@donatasm

About me

  • Originally C# developer @adform
  • Deploying Scala to production for the last 2 years

How did that happen?

Or why would a C# developer search for something beyond .NET?

Online advertising

Scaling is hard

Running everything on Windows and .NET

  • Everything is possible, if you try hard
  • Usually, what you need is already done by someone, but:
    • works only on Linux, or
    • "use at your own risk on Windows", or
    • is not implemented in .NET

..., for example

  • Deployment*
    • Puppet, Docker
  • Fast, scalable and awesome data stores*
    • Cassandra, Aerospike, Vertica, Redis, Kafka
  • High performance libraries and frameworks*
    • clients for the data stores, networking, data processing

* may not be true anymore

Why JVM?

  • Runs everywhere
  • Huge ecosystem of open-source libraries and frameworks

Why not _?

  • Java
  • Clojure
  • Groovy, JRuby, Jython

Quick facts about Scala

  • A scalable language, released in 2004
  • Object-oriented and functional
  • Statically typed with very strong type system
  • Current version 2.11.8

Hello, world!

C#

public static class Hello
{
  public static void Main(string[] args)
  {
    Console.WriteLine("Hello, world!");
  }
}
          

Scala

object Hello {
  def main(args: Array[String]) {
    println("Hello, world!")
  }
}
          

Syntax

C#

interface I {}
class B<T> {}
class A : B<String>, I
{
  int MyMethod() { return 42; }
}
          

Scala

trait T
class B[T]
class A extends B[String] with I {
  def myMethod = 42
}
          

Variables

Scala supports two types of variable declarations:

var declares a variable like in C#

var name = "world"
name = name + "!"
          

val declares a read-only value

val name = "world"
name = name + 1 // ERROR
          

Lazy vals

Scala

lazy val resource = resourceExpensiveToCreate()
          

C#

Lazy<Resource> resource =
  new Lazy<Resource>(
    () => ResourceExpensiveToCreate(),
    isThreadSafe = true);
          

Methods

class Hello {
  def sayTo(name: String): String = {
    return s"Hello, $name"
  }
}
          
class Hello {
  def sayTo(name: String) = s"Hello, $name"
}
          

Methods

val hello = new Hello
hello.sayTo("world")
          
val hello = new Hello
hello sayTo "world"
          

Defining data transfer objects in C#

public sealed class Product
{
  private readonly string _name;
  private readonly string _category;
  private readonly int _price;

  public Product(string name, string category, int price)
  {
    _name = name;
    _category = category;
    _price = price;
  }

  public string Name { get { return _name; }}
  public string Category { get { return _category; }}
  public int Price { get { return _price; }}

  public override bool Equals(object other)
  {
    if (ReferenceEquals(null, other)) return false;
    if (ReferenceEquals(this, other)) return true;
    var o = (Product)other;
    return o._name == _name
      && o._category == _category
      && o._price == _price;
  }

  public override int GetHashCode()
  {
    // magic using number 37, usually generated by tool
  }
}
          

Defining data transfer objects (DTO) in Scala

case class Product(name: String, category: String, price: Int)
          
val p1 = Product("product1", "category1", 99)
val p2 = Product("product1", "category1", 99)
val p3 = Product("product2", "category2", 100)
println(p1)
println(p1 == p2)
println(p2 == p3)
println(p1.price)
          
Product(product1, category1, 99)
true
false
99
          

Transforming DTO in C#

public static class ProductJson
{
  public static JsonObject ToJson(this Product product)
  {
    return new JsonObject(
      new JsonProperty("name", product.Name),
      new JsonProperty("category", product.Category),
      new JsonProperty("price", product.Price)
    );
  }
}
          

And then:

var json = new Product("name", "category", 101).ToJson();
          

Scala implicit conversions

object ProductJson
{
  implicit def toJson(product: Product): JsonObject =
    JsonObject(
      JsonProperty("name", product.Name),
      JsonProperty("category", product.Category),
      JsonProperty("price", product.Price)
    )
}
          

And then:

val json: JsonObject = Product("name", "category", 101);
          

Scala implicit parameters

case class User(id: Int)

class UserRepository {
  def getUserById(id: Int): User = {
    // call to database
  }
}

class UserService {
  def getUserById(id: Int, repository: UserRepository): User = {
    repository.getUserById(id)
  }
}
          

And then:

val repository = new UserRepository()
val service = new UserService()

service.getUserById(1, repository)
          

Scala implicit parameters

class UserService {
  def getUserById(id: Int)(implicit repository: UserRepository): User = {
    repository.getUserById(id)
  }
}
          

And then:

implicit val repository = new UserRepository()
val service = new UserService()

service.getUserById(1)
          

Functions, Actions

C#

Action a1 = () => {};
Action<int> a2 = (int i) => {};
Func<int> f1 = () => 0;
Func<int, int> f2 = (int i) => 0;
          

Scala

val a1 = () => {}
val a2 = (i: Int) => {}
val f1 = () => 0
val f2 = (i: Int) => 0
          

LINQ

C#

var n = Enumerable.Range(1, 10);
n.Select(i => i*i);
n.Where(i => i%2 == 0);
n.Take(3);
n.Count();
n.Distinct();
n.Sum();
          

Scala

val n = 1 to 10
n.map(i => i*i)
n.filter(i => i%2 == 0)
n.take(3)
n.size
n.distinct
n.sum
          

That's (almost) it

  • Convert Java or C# code line by line to Scala, and...
  • you'll have beautiful code. Win!

Tests, for example

test("four bids with the same price, winner is randomized") {
  val adSlot = AdSlot(id = "1", minPrice = Money(1))
  val adSlots = Set(adSlot)
  val bidResponses = List(BidResponse("bidder", bids()))

  val results = (1 to 1000)
    .map(_ => auctioneer.winningBids(adSlots, bidResponses).head)
    .groupBy(_.bid)
    .map { case (_, bids) =>  bids.toSeq.length / 1000.0 }

  all(results) should equal(0.25 +- 0.01)
}
          

Tests, for example

it should "return 400 when request headers " +
  "too large" in withNginxBidder { bidder =>

  val headers = (1 to 8192)
    .map(i => RawHeader(s"X-Header-$i", i.toString))

  val response = bidder
    .client
    .send(Get(s"http://localhost:$nginxPort/")
    .withHeaders(headers: _*))

  response should equal (HttpResponse(400, HttpEntity.Empty,
    List(`Content-Length`(0), Connection("close"))
  ))
}
          

Or DSL for rendiring html

def statsPresentation(stats: Stats) = HttpResponse(
  entity = HttpEntity(`text/html`,
  <html>
    <body>
    <h1>HttpServer Stats</h1>
    <table>
      <tr><td>days:</td><td>{stats.uptime.toDays}</td></tr>
      <tr><td>hours:</td><td>{stats.uptime.toHours}</td></tr>
      <tr><td>minutes:</td><td>{stats.uptime.toMinutes}</td></tr>
      <tr><td>totalRequests:</td><td>{stats.totalRequests}</td></tr>
    </table>
    </body>
  </html>.toString))
          

Or abuse it

When you see this...

					
val result = (0 /: |)(_ + _)
          
					
					
val | = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
          
					
					
val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val result = list.foldLeft(0)(x, y => x + y)
// result = 45
          
					

Patterns

Cake Pattern

The famous pattern of doing DI in Scala

Classical example

case class Size(width: Int, height: Int)
case class Ad(id: Int, size: Size)

trait AdRepository {
  def get(id: Int): Ad
  def find(size: Size): Ad
}
          

Wrap it into component

trait AdRepositoryComponent {
  def adRepository: AdRepository

  trait AdRepository {
    def get(id: Int): Ad
    def find(size: Size): Ad
  }
}
          

Dependency defined as self-type annotation

// self-type annotation
trait Bidder { this: AdRepositoryComponent =>

  def getAd(id: Int): Ad =
    adRepository.get(id)

  def matchAd(size: Size): Ad =
    adRepository.find(size)
}
          

Concrete implementation

trait MySqlAdRepositoryComponent extends AdRepositoryComponent {
  def adRepository = new MySqlAdRepository

  class MySqlAdRepository extends AdRepository {
    override def get(id: Int): Ad = {
      // TODO: get from database
    }

    override def find(size: Size): Ad = {
      // TODO: get from database
    }
  }
}
          

Finally, wiring it up

class DefaultBidder extends Bidder with MySqlAdRepositoryComponent

object BidderApplication {
  def main(args: Array[String]): Unit = {
    val bidder = new DefaultBidder
    // ...
  }
}
          

Testing

class BidderTest extends Bidder with AdRepositoryComponent {
  lazy val adRepository: AdRepository = mock[AdRepository]

  // TODO: write some tests
}
          

Classical constructor injection, pimped with implicits

class DefaultBidder(implicit val repository: AdRepository)

object BidderApplication {
  def main(args: Array[String]): Unit = {
    implicit val repository = new MySqlAdRepository
    val bidder = new DefaultBidder
    // ...
  }
}
          

Yes, but this is Java'ish code!

Scala is also great for OOP

Thank you!

QA

Scala in practice @donatasm