10

From the interview with Kent Beck in a recent Java Magazine issue:

Binstock: Let’s discuss microservices. It seems to me that test-first on microservices would become complicated in the sense that some services, in order to function, will need the presence of a whole bunch of other services. Do you agree?

Beck: It seems like the same set of trade-ofs about having one big class or lots of little classes.

Binstock: Right, except I guess, here you have to use an awful lot of mocks in order to be able to set up a system by which you can test a given service.

Beck: I disagree. If it is in an imperative style, you do have to use a lot of mocks. In a functional style where external dependencies are collected together high up in the call chain, then I don’t think that’s necessary. I think you can get a lot of coverage out of unit tests.

What does he mean? How can functional style liberate you from mocking external dependencies?

  • 1
    see Discuss this ${blog} – gnat Jan 17 '17 at 20:07
  • 1
    If they're discussing Java specifically, I suspect that much of that discussion is moot. Java doesn't really have the kind of support it needs to lend itself to the kind of functional programming being described. Oh, sure, you can use utility classes or perhaps Java 8 Lambdas to simulate it, but... blecch. – Robert Harvey Jan 17 '17 at 20:08
  • 1
8

A pure function is one that:

  1. Will always give the same result given the same arguments
  2. Doesn't have any observable side effects (e.g. state changes)

Suppose we are writing some code to handle user login, where we want to check that the supplied username and password are correct and prevent the user logging in if there are too many failed attempts. In an imperative style our code might look like this:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    if (user == null)
    {
        return false;
    }
    if (user.FailedAttempts > 3)
    {
        return false;
    }
    // Password hashing omitted for brevity
    if (user.Password != password)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return true;
}

Its pretty clear that this is not a pure function:

  1. This function won't always give the same result for a given username and password combination as the result also depends on the user record stored in the database.
  2. The function can change the state of the database, i.e. it has side effects.

Also note that in order to unit test this function we need to mock out two database calls, FindUser and RecordFailedLoginAttempt.

If we were to refactor this code into a more functional style we might end up with something a bit like this:

bool UserLogin(string username, string password)
{
    var user = _database.FindUser(username);
    var result = UserLoginPure(user, password);
    if (result == Result.FailedAttempt)
    {
        _database.RecordFailedLoginAttempt(username);
    }
    return result == Result.Success;
}

Result UserLoginPure(User user, string pasword)
{
    if (user == null)
    {
        return Result.UserNotFound;
    }
    if (user.FailedAttempts > 3)
    {
        return Result.LoginAttemptsExceeded;
    }
    if (user.Password != password)
    {
        return Result.FailedAttempt;        
    }
    return Result.Success;
}

Note that although the UserLogin function is still not pure, the UserLoginPure function is now a pure function and as a result the core user authentication logic can be unit tested without needing to mock any external dependencies. This is because interaction with the database is handled higher up the call stack.

  • Is your interpretation: imperative-style = statefull microservices and functional-style = stateless microservies ? – k3b Jan 18 '17 at 15:35
  • @k3b Sort of, except for the bit about micro services. Very simply Imperative style involves manipulation of state while functional style uses pure functions without state manipulation. – Justin Jan 18 '17 at 18:34
  • 1
    @Justin: I would say that functional style clearly separates pure functions from code with side-effects, as you did in your example. In other words, functional code can still have side effects. – Giorgio Jan 18 '17 at 19:53
  • The functional approach should return a pair with a result and a user, because on a failed attempt, Result.FailedAttempt is the result with a new user with the same data as the original, except it has one more failed attempt and a pure function does induce side effects on the user that is given as a parameter. – risingDarkness Jan 20 '17 at 12:37
  • correction for the last part of my previous comment: "and a pure function does NOT induce side effects on the user that is given as a parameter." – risingDarkness Jan 20 '17 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.