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.
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();
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 variablesMay 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With