Skip to content

Instantly share code, notes, and snippets.

@divs1210
Last active July 3, 2017 16:31
Show Gist options
  • Save divs1210/85ccc95c454253f8f1c5e4e547f9d5b5 to your computer and use it in GitHub Desktop.
Save divs1210/85ccc95c454253f8f1c5e4e547f9d5b5 to your computer and use it in GitHub Desktop.
Dynamic Functional Control Flow Structures for Java
import java.util.*;
import java.util.concurrent.Callable;
import java.util.function.Function;
class SLIP {
/**
* Keywords
*/
public static final Object
_IF = new Object(),
_LET = new Object(),
_WHILE = new Object();
private static final Object
FILLER = new Object();
/**
* Y combinator - Creates recursive lambdas
* */
public static <T,R> Function<T,R> Y(Hopper<T,R> f) {
return SLIP.<T,R> fixer().fix(f);
}
/**
* Functional ternary expression
* */
public static <R> R IF(boolean cond, Callable<R> then, Callable<R> _else) {
List<Callable> condBody = SLIP.list(
(Condition) () -> cond, then,
(Condition) () -> true, _else);
return COND(condBody);
}
/**
* Linear variant for nested IFs
*/
public static <R> R COND(List<Callable> condBodyPairs) {
if (condBodyPairs.isEmpty())
return null;
Condition cond = (Condition) condBodyPairs.get(0);
Callable<R> body = condBodyPairs.get(1);
try {
if (cond.call())
return body.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
return COND(drop(2, condBodyPairs));
}
private static <R> R LET(List<Object> bindings, Body<R> body, Env env) {
if(bindings.isEmpty())
return body.apply(env);
String k = (String) bindings.get(0);
Object v = bindings.get(1);
if(v instanceof DependentBinding)
v = ((DependentBinding) v).apply(env);
env.put(k, v);
return LET(drop(2, bindings), body, env);
}
/**
* Introduce local bindings.
* Bindings down the chain can depend on the ones above
* them by having a DependentBinding as the value.
*
* Supports special bindings: _IF, _LET, and _WHILE
* that work like their counterparts in Clojure's for
* */
public static <R> R LET(List<Object> bindings, Body<R> body) {
return LET(bindings, body, new Env());
}
private static <T> List<T> FOR(List<Object> bindings, Body<Object> body, Env env) {
List<T> res = new ArrayList(1);
if(bindings.isEmpty()) {
Object tmp = body.apply(env);
if (tmp != FILLER)
res.add((T) tmp);
return res;
}
Object _k = bindings.get(0);
Object v = bindings.get(1);
if (_k == SLIP._IF) {
boolean cond = (Boolean) ((Body) v).apply(env);
Body<Object> newBody =
e -> cond?
body.apply(e) :
FILLER;
return FOR(drop(2, bindings), newBody, env);
} else if (_k == SLIP._LET){
return LET((List) v,
(Body<List<T>>) e -> FOR(drop(2, bindings), body, env),
env);
} else if (_k == SLIP._WHILE) {
if ((Boolean) ((Body) v).apply(env))
return FOR(drop(2, bindings), body, env);
else
return res;
}
String k = (String) _k;
if(v instanceof Body)
v = ((Body) v).apply(env);
for(Object o: (Iterable) v){
env.put(k, o);
res.addAll(FOR(drop(2, bindings), body, env));
env.remove(k);
}
return res;
}
/**
* Clojure inspired list comprehension.
* Supports DependentBinding like LET.
*/
public static <T> List<T> FOR(List<Object> bindings, Body<T> body) {
return FOR(bindings, (Body<Object>) body, new Env());
}
// Helpers
/**
* Useful within COND
*/
public static final Condition _else =
() -> true;
/**
* @return A list of Objects
*/
public static List list(Object... objects) {
return Arrays.asList(objects);
}
/**
* @return Numbers from start (inclusive) to
* end (exclusive) in steps of 1.
*/
public static List<Double> range(double start, double end) {
return range(start, end, 1);
}
/**
* @return Numbers from start (inclusive) to
* end (exclusive) in steps of step.
*/
public static List<Double> range(double start, double end, double step) {
List<Double> res = new ArrayList();
try {
final Double[] curr = {start};
Condition shouldStop =
step > 0 ?
() -> curr[0] >= end :
() -> curr[0] <= end;
while (!shouldStop.call()) {
res.add(curr[0]);
curr[0] += step;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return res;
}
/**
* @return list without the first n elements
*/
public static <T> List<T> drop(int n, List<T> list) {
return list.subList(n, list.size());
}
// Internal Helpers
private static <T,R> SelfApply<Fixer<T,R>> combinator() {
return me -> hopper -> input -> hopper.hop(me.self(me).fix(hopper)).apply(input);
}
private static <T,R> Fixer<T,R> fixer() {
final SelfApply<Fixer<T,R>> y = combinator();
return y.self(y);
}
// Supporting Types
public interface Hopper<T,R> {
Function<T,R> hop(Function<T,R> inFunc);
}
private interface Fixer<T,R> {
Function<T,R> fix(Hopper<T,R> toFix);
}
private interface SelfApply<X> {
X self(SelfApply<X> me);
}
// Supporting Type Aliases
public static class Env extends HashMap<String,Object> {}
public interface Condition extends Callable<Boolean> {}
public interface Expression extends Callable<Object> {}
public interface Body<R> extends Function<Env,R> {}
public interface DependentBinding extends Body<Object> {}
}
// Tests
public class Main {
public static void main(String[] args) {
// COND is a linear alternative to
// nested ternary conditionals.
// Bodies can have multiple lines,
// unlike the default ternary operator.
assert SLIP.<Character> COND(SLIP.list(
(SLIP.Condition) () -> 3==0,
(SLIP.Expression) () -> 'a',
(SLIP.Condition) () -> 2==0,
(SLIP.Expression) () -> 'b',
(SLIP.Condition) () -> 1==0,
(SLIP.Expression) () -> 'c',
SLIP._else,
(SLIP.Expression) () -> 'd')
) == 'd';
// Y combinator (recursive lambda) + IF
assert SLIP.<Integer,Integer> Y(
fact ->
n ->
SLIP.IF(n > 1,
() -> n * fact.apply(n - 1),
() -> 1)
).apply(5) == 120;
// LET with dependent bindings
assert SLIP.LET(SLIP.list(
"a", 1,
"b", (SLIP.DependentBinding) env ->
(Integer) env.get("a") + 1),
env ->
(Integer) env.get("b")
) == 2;
// List comprehension
List tuples =
SLIP.FOR(SLIP.list(
"outer", SLIP.range(0, 3),
SLIP._LET, SLIP.list("o", (SLIP.DependentBinding) env ->
env.get("outer")),
SLIP._IF, (SLIP.DependentBinding) env ->
(Double) env.get("o") != 1,
"inner", SLIP.range(0, 30, 10),
SLIP._WHILE, (SLIP.DependentBinding) env -> {
double i = (Double) env.get("inner");
return i < 20;
}),
env -> {
int o = ((Double) env.get("o")).intValue();
int i = ((Double) env.get("inner")).intValue();
return SLIP.list(o, i);
});
assert tuples.equals(
SLIP.list(
SLIP.list(0, 0), SLIP.list(0, 10), // (0, 20) filtered
// all (1, x) filtered
SLIP.list(2, 0), SLIP.list(2, 10) // (2, 20) filtered
)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment