Scala has partial functions which are functions that only apply to some values of the input type, but not all:
val isEven: PartialFunction[Int, String] = {
case x if x % 2 == 0 => x+" is even"
}
assert(isEven(10) equalsIgnoreCase "10 is even")
assert(isEven.isDefinedAt(11) == false)
And, even more useful, scala allows the "partialness" to be applied to the sub-type of a trait
:
sealed trait BaseTrait
case class Foo(i : Int) extends BaseTrait
case class Bar(s : String) extends BaseTrait
val fooPartialFunc : PartialFunction[BaseTrait, Int] = {
case f : Foo => 42 + f.i
}
assert(fooPartialFunc(Foo(8)) == 50)
assert(fooPartialFunc.isDefinedAt(Bar("hello")) == false)
What is the equivalent in java 8?
Most google results confuse "partial function" with currying, e.g. "partially applied function".
The most typical thing in Java 8 is going to be the Optional class:
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import java.util.Optional;
public class OptionalAsPartialFunction {
Optional<String> isEven(final int x) {
return Optional.of(x)
.filter(i -> i % 2 == 0)
.map(i -> i + " is even");
}
@Test
public void example() {
assertThat(isEven(10).get(), equalTo("10 is even"));
assertThat(isEven(11).isPresent(), is(false));
}
}
If you're familiar with Optional, then you'll see that the string concatenation i + " is even"
is only evaluated if the filter condition, i % 2 == 0
, is true. If you're not familiar with with Java's Optional, you can write this out with an if/else
, as well:
Optional<String> isEven(final int x) {
if (x % 2 == 0) {
return Optional.of(x + " is even");
} else {
return Optional.empty();
}
}
This should make is completely clear that the string concatenation is evaluated if and only if the guard condition evaluates to true.
With the naive approach, you're calling the function, and getting an Optional which either contains the actual value of the function or it doesn't. But Scala's PartialFunction is a little bit more complex. From the documentation you linked to:
Even if isDefinedAt returns true for an a: A, calling apply(a) may still throw an exception, so the following code is legal:
val f: PartialFunction[Int, Any] = { case _ => 1/0 }
So we'd like to be able to check whether the function "is defined" for an input, even if trying to compute if for that input would actually be an error.
So a more faithful approach would be to use an Optional<Supplier<...>>
. The outer Optional
lets you know whether there's a computation to perform, and the inner Supplier
lets you perform that computation (if you choose). So the example would become:
Optional<Supplier<String>> isEven(final int x) {
return Optional.of(x)
.filter(i -> i % 2 == 0)
.map(i -> () -> i + " is even");
}
or, with an if/else
:
Optional<Supplier<String>> isEven(final int x) {
if (x % 2 == 0) {
return Optional.of(() -> x + " is even");
} else {
return Optional.empty();
}
}
and isPresent()
still checks whether the function is defined, but get()
will now return the Supplier
whose get()
method will actually compute the value:
@Test
public void example() {
assertThat("isPresent() checks whether the function is defined for the input", //
isEven(10).isPresent(), equalTo(true));
assertThat("get() returns the supplier that actually computes the value", //
isEven(10).get().get(), equalTo("10 is even"));
assertThat("isPresent() checks whether the function is defined for the input", //
isEven(11).isPresent(), is(false));
}
Java does not seem to provide PartialFunction
s directly, but it provides a few interfaces that you can use to define your own PartialFunction<X,Y>
. A PartialFunction<X, Y>
is essentially just:
Predicate<X>
for testing whether a value of type X
is in the domainFunction<X, Y>
for the actual function definition that is invoked if the function is defined for the argument value.Below, I've sketched how you could make use of Predicate
and Function
to implement PartialFunction
. In this sketch, I defined only the three most important methods:
isDefinedAt
is essentially just Predicate
s test
.apply
invokes test
and, if successful, invokes the applyIfDefined
, which represents the "actual body of the function" (right hand side of case
s in Scala)orElse
is a demonstration of a compositional structure different from ordinary function composition, just as a proof of conceptHere is the full Java code:
import java.util.function.*;
abstract class PartialFunction<X, Y> implements Predicate<X>, Function<X, Y> {
public boolean isDefinedAt(X x) {
return this.test(x);
}
public Y apply(X x) {
if (isDefinedAt(x)) {
return applyIfDefined(x);
} else {
throw new IllegalArgumentException("Match error on " + x);
}
}
public abstract Y applyIfDefined(X x);
public PartialFunction<X, Y> orElse(PartialFunction<X, Y> fallback) {
PartialFunction<X, Y> outer = this;
return new PartialFunction<X, Y>(){
public boolean test(X x) {
return outer.test(x) || fallback.test(x);
}
public Y applyIfDefined(X x) {
if (outer.isDefinedAt(x)) {
return outer.applyIfDefined(x);
} else {
return fallback.apply(x);
}
}
@Override
public Y apply(X x) {
return applyIfDefined(x);
}
};
}
public static void main(String[] args) {
PartialFunction<Integer, String> f = new PartialFunction<Integer, String>() {
public boolean test(Integer i) {
return i % 2 == 0;
}
public String applyIfDefined(Integer i) {
return i + " is even";
}
};
PartialFunction<Integer, String> g = new PartialFunction<Integer, String>() {
public boolean test(Integer i) {
return i % 2 == 1;
}
public String applyIfDefined(Integer i) {
return i + " is odd";
}
};
System.out.println(f.apply(42));
try {
System.out.println(f.apply(43));
} catch (Exception e) {
System.out.println(e);
}
PartialFunction<Integer, String> h = f.orElse(g);
System.out.println(h.apply(100));
System.out.println(h.apply(101));
}
}
The output of the above example is:
42 is even
java.lang.IllegalArgumentException: Match error on 43
100 is even
101 is odd
If you wanted to use it with some kind of "case-classes", you could do this too, but for that, you would have to provide an implementation of the case classes first.
Vavr brings to Java a lot of cool stuff from Scala.
If you ok with 3rd party libraries, it could be the best choice.
@Test
public void test() {
PartialFunction<Integer, String> isEven =
Function1.<Integer, String>of(integer -> integer + " is even")
.partial(integer -> integer % 2 == 0);
Assert.assertEquals("10 is even", isEven.apply(10));
Assert.assertFalse(isEven.isDefinedAt(11));
}
More complex example you can find here
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