package com.github.rfqu.so;
import java.util.Random;
/**
* To illustrate discussion at
* http://stackoverflow.com/questions/13080042/the-right-way-to-make-java-function-objects-to-encapsulate-generic-functions
* Author: Alexei Kaigorodov
*
* All the functionality is in value types (Int and Rational)
* No separate Group or Ring classes used.
* "Double dispatch" programming pattern is used.
*/
public class SoFun2 {
static interface Unary<E,R> {
R ap(E a) ;
}
static interface Binary<E,R> {
R ap(E a, E b) ;
}
static interface Applier {
Int ap (Int x, Int y);
Rational ap (Rational x, Rational y);
}
static class PlusApplier implements Applier {
@Override
public Int ap(Int x, Int y) {
return x.plus(y);
}
@Override
public Rational ap(Rational x, Rational y) {
return x.plus(y);
}
}
static class TimesApplier implements Applier {
@Override
public Int ap(Int x, Int y) {
return x.times(y);
}
@Override
public Rational ap(Rational x, Rational y) {
return x.times(y);
}
}
static interface Applicable<E> {
E apply(Applier applier, E y);
}
static class Int implements Applicable<Int> {
int x;
public Int(int x) {
this.x = x;
}
@Override
public Int apply (Applier applier, Int other) {
return applier.ap(this, other);
}
public Int plus(Int other) {
return new Int(x+other.x);
}
public Int times(Int other) {
return new Int(x*other.x);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Int)) return false;
Int other=(Int)obj;
return x==other.x;
}
}
static class Rational implements Applicable<Rational> {
int x, y; // x/y
public Rational(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public Rational apply(Applier applier, Rational other) {
return applier.ap(this, other);
}
public Rational plus(Rational op2) {
return new Rational(x*op2.y+y*op2.x, y*op2.y);
}
public Rational times(Rational op2) {
return new Rational(x*op2.x, y*op2.y);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Rational)) return false;
Rational other=(Rational)obj;
return x*other.y==y*other.x;
}
}
static class F implements Unary<Int, Rational> {
public Rational ap (Int x) { return new Rational(x.x,2); }
}
static <E extends Applicable<E>, R extends Applicable<R>>
boolean checkCommutesWith(
Unary<E,R> f
, Applier g
, E x, E y)
{
E g_ap1 = x.apply(g, y);
R f_ap_x = f.ap(x), f_ap_y = f.ap(y);
R g_ap2 = f_ap_x.apply(g, f_ap_y);
return f.ap(g_ap1).equals(g_ap2);
}
public static void main(String[] args) {
Random rand=new Random();
boolean res = checkCommutesWith(new F(), new PlusApplier(),
new Int(rand.nextInt()), new Int(rand.nextInt()));
System.out.println("res="+res);
res = checkCommutesWith(new F(), new TimesApplier(),
new Int(rand.nextInt()), new Int(rand.nextInt()));
System.out.println("res="+res);
}
}