Created
October 31, 2024 03:46
-
-
Save gigamonkey/5d35c597cafa49f284d66e7fa5f00116 to your computer and use it in GitHub Desktop.
Demo of lift functions in Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.*; | |
import java.util.function.*; | |
public class Lift { | |
public static interface Expression { | |
public Optional<Double> evaluate(); | |
} | |
// This is a higher-order function that takes a function (i.e. an istance of a | |
// functional interface) and abstracts the pattern of flat mapping and mapping | |
// we discussed today for functions that take two arguments. | |
// | |
// In this function we can use the existing DoubleBinaryOperator and | |
// BinaryOperator<T> from java.util.function. | |
// | |
// The name liftM2 comes from the Haskell programming language which is big | |
// into this kind of thing. I think the idea is that you're "lfiting" the | |
// function from its earthly realm of dealing with actual values into a higher | |
// plane of dealing with Optionals of values. The 2 stands for the number of | |
// arguments. | |
public static BinaryOperator<Optional<Double>> liftM2(DoubleBinaryOperator fn) { | |
return (o1, o2) -> o1.flatMap(d1 -> o2.map(d2 -> fn.applyAsDouble(d1, d2))); | |
} | |
// Now let's define two new functional interfaces as analogs to | |
// DoubleBinaryOperator and BinaryOperator<T> but for three-arguments | |
// functions: | |
public static interface DoubleTrinaryOperator { | |
public double apply(double d1, double d2, double d3); | |
} | |
public static interface TrinaryOperator<T> { | |
public T apply(T a, T b, T c); | |
} | |
// Now we can define liftM3 which can lift a three-arg function into the Optional realm. | |
public static TrinaryOperator<Optional<Double>> liftM3(DoubleTrinaryOperator fn) { | |
return (o1, o2, o3) -> o1.flatMap(d1 -> o2.flatMap(d2 -> o3.map(d3 -> fn.apply(d1, d2, d3)))); | |
} | |
// This is kind of a goofy example because the whole point of why you went | |
// down this path was to trap the case where the discriminant is negative. But | |
// this shows off the liftM3 mechanism. | |
record QuadraticfFormula(Expression a, Expression b, Expression c) implements Expression { | |
public Optional<Double> evaluate() { | |
return liftM3(this::formula).apply(a.evaluate(), b.evaluate(), c.evaluate()); | |
} | |
private double formula(double a, double b, double c) { | |
return (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a); | |
} | |
} | |
record Number(double value) implements Expression { | |
public Optional<Double> evaluate() { return Optional.of(value); } | |
} | |
public static void main(String[] args) { | |
System.out.println(new QuadraticfFormula(new Number(5), new Number(6), new Number(1)).evaluate()); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment