Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functional programming in OOP language

immutable objects are OK, but, are OK non-final local references?

In other words, next code snippet can be represented as written in functional style?

Employee e = new Employee("Lex", 24, 250);
e = Employee.setName(e, "Vasili");
e = Employee.setAge(e, 12);
e = Employee.setSalary(e, 2500);
Employee.log(e);

P.S. here all Employee methods are static, setters are factory methods that returns new instances.

like image 311
Lex Ushakov Avatar asked Jun 24 '26 15:06

Lex Ushakov


2 Answers

Since this question is tagged 'java', I assume the question is on the FP practices (namely, immutability) in Java.

Today's good practice in Java is to use either a builder:

Employee e = Employee.builder()
                     .surname("Lex")
                     .age(24)
                     .name("Vasili")
                     .salary(2500)
                     .build();

or a static constructor:

Employee e = Employee.of("Vasili", "Lex", 24, 2500);

In both cases, the "classic" constructor should be declared private to ensure that the object can't be instantiated and made available to the client in inconsistent state.

Object mutators should then return the new object:

Employee.of("Vasili", "Lex", 24, 2500)  // creates an object
        .updateName("Sergey")           // returns 1st modified copy
        .updateSalary(3500);            // returns 2nd modified copy based on 1st copy

Following these practices, the need for non-final local references often vanishes.
A very popular example is the Date and Time API.

Now, on using mutable local variables. That's ok, but the code can be shortened and made more expressive using method chaining. Trying to chain the static methods as-is will not look very elegant:

Employee e = setSalary(setAge(setName(new Employee("Lex", 24, 250), "Vasili"), 12), 2500);

As an attempt to emulate a monad, one can wrap the object in some monad-like container which defines a bind method that accepts a function, which will accept the object stored in monad and return some result, which will again be wrapped in a monad. A simple example will look like this:

static class Employee {
    public String name;
    public int age;
    public long salary;
}

static class Monad<T> {
    private final T value;

    private Monad(T value) { this.value = value; }

    public static <T> Monad<T> of(T value) {
        return new Monad<>(value);
    }

    public T getValue() { return value; }

    public Monad<T> bind(UnaryOperator<T> operator){
        return of(operator.apply(value));
    }
}

public static void main(String[] args) {
    Employee value = Monad.of(new Employee())
                          .bind(e -> {e.name = "Lex"; return e; })
                          .bind(e -> {e.age = 24; return e; })
                          .bind(e -> {e.salary = 2500; return e; })
                          .getValue();
}

But this can be done with core Java since version 8 - Stream API can do this and much more:

Stream.of(new Employee())
      .map(e -> {e.name = "Lex"; return e; })
      .map(e -> {e.age = 24; return e; })
      .map(e -> {e.salary = 2500; return e; })
      .findFirst()
      .get();
like image 61
jihor Avatar answered Jun 27 '26 06:06

jihor


Don't know anything about Haskell, but I believe you are trying to achieve something like this:

Employee e = new Employee("Lex")
    .setAge(25)
    .setSalary(2500)
    .setGender(Gender.Male);

This is just a result of chaining functions in the following way

public Employee setParam(param){
   this.param = param;
   return this;
}

but the methods are not static, they belong to the instance.

Also there would be no need to pass the instance as parameter.


Also:

  • this isn't a required keyword; in my example above, the two parameters have the same name, so without this the code would basically re-assign param's value to itself. If the parameters had a different name this is not required. Yet returning this is necessary as it represent a reference to the current instance.

E.g:

public Employee setParam(String param) {
    parameter = param; // parameter is a field in class Employee
    return this; // this "this", is still necessary
}
  • final variables

May limit you on what you are trying to achieve with your style

final Employee e = Employee.setName(e, "Name"); // invalid, e is unkown

// ----------------

final Employee e;
e = Employee.setName(e, "Name"); // invalid, e may not be initialized

// ----------------

final Employee e;
e = Employee.setName("Name"); // valid

// ----------------

final Employee e = null;
e = Employee.setName(e, "Name"); // invalid. e was already initalized to null

// ----------------

final Employee e = Employee.setName("Name"); // valid
e = Employee.setName("Name2"); // invalid, final variable already initialized
like image 29
Marko Pacak Avatar answered Jun 27 '26 05:06

Marko Pacak



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!