What is pure function?

What is pure function?

What is pure function? According to Wikipedia:

  1. The function always evaluates the same result value given the same argument value(s). (…)
  2. Evaluation of the result does not cause any semantically observable side effect or output (…).

The first point seems to be clear. It’s a ‘natural’ definition of a mathematical function we learn in the primary school. When can a function yield a different result given the same arguments? Only when it calls impure functions or depends on the external state in the calculations.

The second point mentions semantically observable side effect or output. First of all, what does semantically observed stand for?

Semantics is a branch of science that deals with meaning – a connection between a symbol and something in our senses or mind that the symbol expresses. We associate the word bird with a flying animal covered by feathers. In a Java program, we know that the + symbol stands for addition of numbers or concatenation of strings. Note that this is not the same algebraic operation; numeric addition is commutative, string concatenation is not.

So, for me, semantically observed means (pun intended) being significant in our program.

What is then a side effect? When we modify a global variable, we cause a side effect. Same with calling a setter on an object passed to us. Writing to a file is usually considered a side effect too. Global variables, all living objects during the execution of a program, current content of the filesystem – it’s all state. Side effects are changes to the external state from the perspective of a function that will stay visible after the function is executed.

Let’s see some examples (in ES6). It this function pure?

array => {
  array.reverse();
  // more transformations of the array
}

It uses the Array.reverse method that works in-place. Array object passed to the function is going to be modified – we are changing the external state of the function. Final verdict – we are dealing with an impure function.

Another, slightly modified version of this function:

array => {
  const clone = a => a.slice(0);
  const reversed = clone(array).reverse();
  // more transformations of the array
}

It makes a (shallow) copy of the array argument before mutating it. Assuming that subsequent array transformations are pure, i.e. they don’t mutate the array elements, this function can be considered pure. Seen from the outside, there is no modification of state. array, the only element of the external state known to our function, remains unchanged.

Conclusion

If you want to be pure, don’t get your hands dirty with the outside world:

  • don’t rely on it in your calculations (point 1)
  • don’t try to change it (point 2).

Additional reading

Advertisements

Coordinated Mocha setup and teardown

Playing with Node.js, Express and WebDriver I spotted duplication in my Mocha tests. Each test started/stopped an Express application and created/destroyed a WebDriver client:

before(() => {
  startServer();
  createWebdriver();
});

after(() => {
  stopServer();
  destroyWebdriver();
});

var server, driver;

const startServer = () => server = app.listen(3000);
const stopServer = () => {
  var deferred = Q.defer();
  server.close(() => deferred.resolve());
  return deferred.promise;
};

const createWebdriver = () => {
  driver = new webdriver.Builder()
    .forBrowser('firefox')
    .build();
};
const destroyWebdriver = () => driver.quit();

Obviously, I could move startServer/stopServer and createWebdriver/destroyWebdriver pairs to a separate module and require them in each test. This would have partially solved the duplication, since those methods were defined only in one place.

However, test setup and teardown methods (before and after) are still duplicated in each test case. They share the state (server handle and WebDriver client) and are sequentially coupled. Each launched server instance must be shut down. Same with the WebDriver client. Moreover, I cannot separate setup and teardown steps and reuse e.g. only the server launch.

How can it be solved?

First, you can have more then one Mocha setup and teardown lifecycle function.

Second, statements in anonymous functions passed to before and after are coincidentally cohesive. They have been put together only because they need to be executed at the beginning/end of the test case. This is equivalent to the previous snippet:

before(() => startServer());
before(() => createWebdriver());

after(() => stopServer());
after(() => destroyWebdriver());

Third, functions in JavaScript are first-class citizens. They can be passed around and executed in a different context.

Knowing all this, what’s the final solution?

Coordinate cohesive setup/teardown code in a module. Pass setup and teardown functions to the module and execute them when creating the module instance. Export created or initialised objects if your test methods have to use them.

And the refactored code:

// in test
require('./server')(before, after);
const {driver} = require('./driver')(before, after);

// server.js
module.exports = (setupFn, teardownFn) => {
  var server;

  const startServer = () => server = app.listen(port);

  const stopServer = () => {
    var deferred = Q.defer();
    server.close(() => deferred.resolve());
    return deferred.promise;
  };

  setupFn(() => startServer());

  teardownFn(() => stopServer();
};

// driver.js
module.exports = (setupFn, teardownFn) => {
  var driver;

  const createWebdriver = () => {
    driver = new webdriver.Builder()
      .forBrowser('firefox')
      .build();
  };

  const destroyWebdriver = () => driver.quit();

  setupFn(() => createWebDriver());

  teardownFn(() => destroyWebdriver());

  return {
    driver: () => driver
  };
};

Notice, that in the original code, driver was a WebDriver client instance (an object with properties and methods). After the refactoring, driver is a function that returns the WebDriver client from the module instance. I cannot expose the WebDriver client directly (i.e. as return { driver: driver }). because after the module evaluation it is still undefined.

Conclusion

Passing Mocha lifecycle functions around removed duplication, put sequential coupling under control and allowed composability of test setup and teardown routines.

Extract method refactor with Java 8 default method

Java 8 default methods can be a handy tool for refactoring interface contracts. You can use this trick in case you want to add a new interface method that can be expressed in terms of an existing one. You don’t want to change the implementors of the interface (at least for now).

Let’s take an example on a interface (Loader) with a mis-defined contract. Instead of getting levels where a Loader can be applied to, I wanted the loader to answer this question directly.

The initial code was as follows:

public interface Loader {
  void load(Page page);

  List<Level> getLevelsToBeAppliedOn();
}

boolean shouldBeApplied(Loader loader, Level level) {
    return loader.getLevelsToBeAppliedOn().stream().anyMatch(level::equals);
}

After the refactor, the Loader interface gets a new default method, shouldBeAppliedOn that delegates to the existing method, getLevelsToBeAppliedOn:

public interface Loader {
  void load(Page page);

  List<Level> getLevelsToBeAppliedOn();

  default boolean shouldBeAppliedOn(Level other) {
    return getLevelsToBeAppliedOn().stream().anyMatch(other::equals);
  }
}

The client code is simplified:

boolean shouldBeApplied(Loader loader, Level level) {
  return loader.shouldBeAppliedOn(level);
}

Now this method doesn’t make our intent any clearer and can be safely inlined.

This refactoring didn’t break the implementors of the Loader interface. The next step would be to implement the shouldBeAppliedOn method in all Loader derivates. When done, I could convert the default method to an abstract method (i.e. remove its body) and remove the original method (getLevelsToBeAppliedOn).

Things I learned about ReactiveX and RxJava

Things I learned about ReactiveX and RxJava

Some loose notes about what I learned about ReactiveX and RxJava while developing Vitamin-Rx, a simplified clone of Vitamin-R 2.

Hot and cold Observables

When creating an Interval Observable, I thought it will start ticking (emitting) immediately after creation. Wrong! There are hot and cold Observables. Hot Observables start firing events immediately. Cold Observables, only when there is at least one subscriber. Interval is cold – you have to connect an Observer first.

Operators

Docs and marble diagrams are the best source of information about transforming Observables.

Pure streams and side effects

When you connect an Observer to an Observable, every item emitted by the source is passed to the onNext Observer method. There are no restrictions on what you can put in the onNext handler. On the other hand, there are do callbacks, which can act similarly to onNext.

What’s the best practice? Implement onNext handlers as pure functions. Actions with side-effects go into doOnNext callbacks.

In my pet project, TimeSlice class creates a tick stream transformed previously with pure functions. Main class adds impure console and Growl notifications to the stream in do callbacks. In this way I can unit test the Observable produced by the TimeSlice class using the provided TestScheduler (more on this in the next section).

Testing

Want to be a master of time? Test your Observables with TestScheduler. You will be able to advance time and gather all items produced by the Observable under test. Check the TimeSliceTest class for usage examples.

Backpressure

Backpressure is a way to deal with an Observable that produces items too rapidly (or too slowly).

In my case I wanted to pause the interval Observable and resume its activity. I didn’t find a direct way (i.e. through any of RxJava interfaces), so I asked on Stack Overflow. The solution consists in filtering the Observable by a boolean “semaphore” toggled from outside.

Implement custom option handler in args4j

In the previous post about args4j, I showed how to write a custom multi-value option handler based on a built-in handlers.

This post guides you through writing your own option/argument handler. I’m going to show you an example of a handler that sets a java.time.Duration option.

First of all, you have to decide which base class to extend: either OptionHander or OneArgumentOptionHandler. I preferred the latter one, because it can be used for multi-value options (with a DelimitedOptionHandler). In both cases you must implement a ‘boilerplate’ constructor that delegates to the superclass. Here is the stub of the handler:

public class SimplifiedDurationHandler extends OneArgumentOptionHandler<Duration> {
    public SimplifiedDurationHandler(CmdLineParser parser, OptionDef option, Setter<? super Duration> setter) {
        super(parser, option, setter);
    }

    @Override
    protected Duration parse(String argument) throws CmdLineException {
        // ...
    }
}

The next step is to implement the actual parse method. Extending the OneArgumentOptionHandler gives another advantage. You don’t have to deal with Setters. You just return the parsed value and the base class will set the option.

In my example, I’m going to match the argument against a regular expression. In case of a successful match, I create a Duration from the amount and unit parts. In case of a mis-match, I signal the error to the library by throwing a CmdlineException:

@Override
protected Duration parse(String argument) throws CmdLineException {
    Matcher matcher = PATTERN.matcher(argument);
    if (!matcher.matches()) {
        throw misformattedArgumentException(argument);
    }
    return Duration.of(Integer.valueOf(amount(matcher)), unit(matcher));
}

private CmdLineException misformattedArgumentException(String token) {
    return new CmdLineException(owner, Messages.ILLEGAL_OPERAND, option.toString(), token);
}

By passing a Messages.ILLEGAL_OPERAND and option.toString() to the CmdLineException constructor, you’ll get a nice error message:

"1h" is not a valid value for "-d (--duration)"

for an @Option, or:

"1h" is not a valid value for "duration"

for an @Argument.

You can see the full source code together with an integration test on Github: