Cogs and Levers A blog full of technical stuff

Futures and Promises in Scala

The wikipedia article for Futures and promises opens up with this paragraph, which I thought is the perfect definition:

In computer science, future, promise, delay, and deferred refer to constructs used for synchronizing program execution in some concurrent programming languages. They describe an object that acts as a proxy for a result that is initially unknown, usually because the computation of its value is yet incomplete.

In today’s article, I’ll walk you through the creation and management of the future and promise construct in the Scala language.

Execution context

Before continuing with the article, we need to make a special note about the ExecutionContext. Futures and promises both use the execution context to perform the execution of their computations.

Any of the operations that you’ll write out to start a computation requires an ExecutionContext as a parameter. These can be passed implicitly, so it’ll be a regular occurrence where you’ll see the following definition:

// define the implicit yourself
implicit val ec: ExecutionContext = ExecutionContext.global

// or - import one already defined
import ExecutionContext.Implicits.global

ExecutionContext.global is an ExecutionContext that is backed by a ForkJoinPool.

Futures

We create a Future in the following ways:

/* Create a future that relies on some work being done
   and that emits its value */
val getName = Future {
  // simulate some work here
  Thread.sleep(100)
  "John"
}

/* Create an already resolved future; no need to wait
   on the result of this one */
val alreadyGotName = Future.successful("James")

/* Create an already rejected future */
val badNews = Future.failed(new Exception("Something went wrong"))

With a future, you set some code in place to handle both the success and fail cases. You use the onComplete function to accomplish this:

getName onComplete {
  case Success(name) => println(s"Successfully got $name")
  case Failure(e) => e.printStackTrace()
}

Using a for-comprehension or map/flatMap, you can perform functional composition on your Future so that adds something extra through the pipeline. In this case, we’re going to prefix the name with a message should it start with the letter “J”:

val greeting = for {
  name <- getName
  if name.startsWith("J")
} yield s"Hello there, $name!"

Blocking

If you really need to, you can make your future block.

val blockedForThisName = Future {
  blocking {
    "Simon"
  }
}

Promises

The different between a Future and a Promise is that a future can be thought of as a read-only container. A promise is a single-assignment container that is used to complete a future.

Here’s an example.

val getNameFuture = Future { "Tom" }
val getNamePromise = Promise[String]()

getNamePromise completeWith getNameFuture

getNamePromise.future.onComplete {
  case Success(name) => println(s"Got the name: $name")
  case Failure(e) => e.printStackTrace()
}

getNamePromise has a future that we access through the future member. We treat it as usual with onComplete. It knows that it needs to resolve because of the completeWith call, were we’re telling getNamePromise to finish the getNameFuture future.

Getting functional with Java 8

Java 8 provides developers with functional programming facilities that are very new to the language itself.

In today’s post, I’ll go through a primer of the different facilities that you can use.

Functional Interfaces

Single abstract method interfaces have been taken a step further in Java 8, where the programmer is able to decorate their interface using a @FunctionalInterface annotation. These can then be represented as lambda expressions and method references. These are building blocks for functional programming.

Consumer<T>

Represents an operation that accepts a single input argument and returns no result. Unlike most other functional interfaces, Consumer is expected to operate via side-effects.

Uses are passing to the forEach function.

Supplier<T>

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

Used in the creation of streams.

Predicate<T>

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

Used with filter

Function<T, R>

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

Used to pass to map

Functional Syntax

Defining a Functional Interface

@FunctionalInterface
public interface TailCall<T> {
  
  TailCall<T> apply();

  default boolean isComplete() { return false; }

}

A functional interface must:

  • Have one abstract, unimplemented method
  • May have zero or more default or implemented methods
  • May have static methods

Lambda expressions

// no parameters
() -> func(1)

// explicit definition of parameter types
(final String colour) -> System.out.println(colour)

// implicit parameter definition
(colour) -> System.out.println(colour)
colour -> System.out.println(colour)

// multiples
(x, y) -> x * y

// multi-line lambda
(x) -> {
  func1(x);
  func2(x);
}

Composition, method references

products.stream()
        .filter(Pricing::hasDiscount)
        .mapToDouble(Product::getUnitPrice)
        .average()
        .getAsDouble();

Functors in Scala

Scala’s type is very rich; even where constructs aren’t well defined you can easily piece together anything that you need. In today’s article, I’ll take you through some different functor types as well as a small primer on variance.

Variance

Type variance is what we use to describe sub-class relationships in a class hierarchy. In scala we use the following notations to denote variances:

  Description Notation
covariant C[T'] is a subclass of C[T] [+T]
contravariant C[T] is a subclass of C[T'] [-T]
invariant C[T] and C[T'] are not related [T]

Functors

Covariant functors are what provide map or fmap:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B): F[A] => F[B]
}

Contravariant functors provide contramap:

trait Contravariant[F[_]] {
  def contramap[A, B](f: B => A): F[A] => F[B]
}

Exponential functors are what provide xmap:

trait Exponential[F[_]] {
  def xmap[A, B](f: (A => B, B => A)): F[A] => F[B]
}

Applicative functors are what provide apply and <*>:

trait Applicative[F[_]] {
  def apply[A, B](f: F[A => B]): F[A] => F[B]
}

Monads provide bind, flatMap or =<<:

trait Monad[F[_]] {
  def flatMap[A, B](f: A => F[B]): F[A] => F[B]
}

Comonads provide extend, coflatMap and <<=:

trait Comonad[F[_]] {
  def coflatMap[A, B](f: F[A] => B): F[A] => F[B]
}

Unit testing private members in Java

Sometimes, you need to be able to look at the private members of your classes in order to test that something has gone to plan. Unit testing is one scenario where this makes sense.

import java.lang.reflect.Field;

. . .

private static byte[] getAddressSpace(Memory m) {
    try {
        Field field = Memory.class.getDeclaredField("addressSpace");
        field.setAccessible(true);
        return (byte[]) field.get(m);
    } catch (Exception e) {
        fail(e.getMessage());
    }

    return null;
}

By using the getDeclaredField method, passing the name of the field; the reflection framework will send back the definition. This field gets executed through the use of the get method, passing in the object instance.

To finish the picture here, we can also get access on methods that are private as well:

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

Done.

Streams in Java 8

The functional programming paradigm has certainly seen a lot of attention of late, where some of the features that can be exploited from it have properties that assist in scale programming.

In today’s post, I’ll walk through Java 8 streams which allow you to treat collection data structures in a functional way.

What is a stream?

A stream prepares a collection of elements in such a way that you can operate on it functionally. The higher-order functions that you’d commonly see in a functional arrangement are:

An example

To start with, we need to define some test data. We’re going to work with artists and songs:

/** Song.java */
public class Song {
  public String name;
  public int year;
  public int sales;

  public Song(String name, int year, int sales) {
    this.name = name;
    this.year = year;
    this.sales = sales;
  }

  public String getName() {
    return name;
  }

  public int getYear() {
    return year;
  }

  public int getSales() {
    return sales;
  }
}

/* Artist.java */
public class Artist {
  private String name;
  private int age;
  private List<Song> songs;

  public Artist(String name, int age, List<Song> songs) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public int getAge() {
    return age;
  }

  public List<Song> getSongs() {
    return songs;
  }
}

Now that we have a basic structure of artists and songs, we can define some test data to work with.

Don’t judge me.

Artist rickAstley = new Artist(
  "Rick Astley",
  50,
  Arrays.asList(
    new Song(
      "Never Gonna Give You Up",
      1987,
      500000
    )
  )
);

Artist bonJovi = new Artist(
  "Jon Bon Jovi",
  54,
  Arrays.asList(
    new Song(
      "Livin' on a Prayer",
      1986,
      3400000
    ),
    new Song(
      "Wanted Dead or Alive",
      1987,
      4000000
    )
  )
);

List<Artist> artists = Arrays.asList(bonJovi, rickAstley);

Ok, now that the boilerplate is out of the way; we can start the fun stuff.

Map

map is a function that allows you to apply a function to each element of a list; transforming and returning it. We can grab just the artist’s names with the following:

artists.stream()
  .map(Artist::getName)
  .collect(toList())

toList comes out of the java.util.stream.Collectors class. So you can import static:

import static java.util.stream.Collectors.toList;

Mapping deeper

flatMap allows you to perform the map process on arrays of arrays. Each artist has an array of songs, so we can flat map (at the artist level) to emit a flat list of songs:

artists.stream()
  .flatMap(a -> a.getSongs().stream())
  .map(Song::getName)
  .collect(toList())

Filter

We only want artists over the age of 52.

artists.stream()
  .filter(a -> a.getAge() > 52)
  .map(Artist::getName)
  .collect(toList())

Reduce

We can aggregate all of the sales figures into a single value:

artists.stream()
  .flatMap(a -> a.getSongs().stream())
  .mapToInt(Song::getSales)
  .sum()

Ok, so this has been a whirlwind tour of the Java 8 functional interface.