blob: 5c7db20c91bc5fec48c3d076caa7685aed2c0e1b [file] [log] [blame]
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.apple.internal.jobjc.generator.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Functional programming constructs and utilities. Java for Lisp and Haskell nerds.
*/
public abstract class Fp {
/**
* Multiple dynamic dispatch (multi-methods) for Java.
*
* This is implemented with Java reflection:
* Class.getDeclaredMethod and Method.invoke.
* It is about 20-40 times slower than chains of
* "if instanceof" statements.
*/
public static abstract class Dispatcher{
/**
* Shorthand, works only if no arg is null.
*/
public static final <R> R dispatch(Class clazz, Object instance, String method, Object... args) throws NoSuchMethodException{
Class[] types = new Class[args.length];
for(int i = 0; i < args.length; i++) types[i] = args[i].getClass();
return (R) dispatch(clazz, instance, method, args, types);
}
/**
* Dispatch `args` of `types` to `method` on `clazz` for `instance`. If `method` is static, `instance` should be null.
*/
public static final <R> R dispatch(Class clazz, Object instance, String method, Object[] args, Class[] types) throws NoSuchMethodException{
try{
java.lang.reflect.Method m = clazz.getDeclaredMethod(method, types);
m.setAccessible(true);
return (R) m.invoke(instance, args);
}
catch(NoSuchMethodException x){
if(clazz.getSuperclass() != null) return (R) dispatch(clazz.getSuperclass(), instance, method, args, types);
else throw x;
}
catch(Exception x){
throw new RuntimeException(x);
}
}
}
/**
* The "Maybe" type encapsulates an optional value. A value of type
* "Maybe a" either contains a value of type "a" (represented as "Just a"),
* or it is empty (represented as "Nothing").
*
* http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Maybe.html
*/
public static abstract class Maybe<A>{
public abstract boolean isJust();
public abstract boolean isNothing();
public abstract A fromJust() throws ClassCastException;
public abstract A fromMaybe(final A fallback);
public static class Nothing<A> extends Maybe<A>{
@Override public A fromJust() throws ClassCastException { throw new ClassCastException("Cannot extract value from Nothing."); }
@Override public A fromMaybe(A fallback) { return fallback; }
@Override public boolean isJust() { return false; }
@Override public boolean isNothing() { return true; }
}
public static class Just<A> extends Maybe<A>{
public final A a;
public Just(A a){ this.a = a; }
@Override public A fromJust(){ return a; }
@Override public A fromMaybe(A fallback) { return a; }
@Override public boolean isJust() { return true; }
@Override public boolean isNothing() { return false; }
}
}
public static class NonNull<A>{
public final A obj;
public NonNull(A o){
if(o==null) throw new RuntimeException("o may not be null.");
this.obj = o;
}
}
// Closures
public static interface Map0<A>{ A apply(); }
public static interface Map1<A,B>{ B apply(final A a); }
public static interface Map2<A,B,C>{ C apply(final A a, final B b); }
public static class CacheMap<K extends Comparable<K>,V>{
private Map<K,V> cache = new TreeMap<K,V>();
public V get(K key, Map0<V> create){
if(cache.containsKey(key)) return cache.get(key);
V value = create.apply();
cache.put(key, value);
return value;
}
}
public static class Curry2to1<A,B,C> implements Map1<B,C>{
private Map2<A,B,C> target; private A a;
public Curry2to1(Map2<A, B, C> targett, A aa) { target = targett; a = aa; }
public C apply(B b) { return target.apply(a, b); }
}
// Tuple
public static class Pair <A,B> implements Comparable<Pair<A,B>>{
public final A a; public final B b;
public Pair(final A aa, final B bb){ a=aa; b=bb; }
@Override public int hashCode(){ return (a==null ? 0 : a.hashCode()) + (b==null ? 0 : b.hashCode()); }
@Override public boolean equals(Object o){
if(!(o instanceof Pair)) return false;
Pair<?,?> p = (Pair<?,?>) o;
return QA.bothNullOrEquals(a, p.a) && QA.bothNullOrEquals(b, p.b);
}
@Override public String toString(){ return "(" + a + ", " + b + ")"; }
public int compareTo(Pair<A, B> o){ return toString().compareTo(o.toString()); }
}
/**
* @return [fn(x) | x <- items]
*/
public static <A,B> List<B> map(Map1<A,B> fn, final Collection<A> xs){
ArrayList<B> rs = new ArrayList<B>(xs.size());
for(A x : xs) rs.add(fn.apply(x));
return rs;
}
public static <A,B,C> List<C> map2(Map2<A,B,C> fn, final Collection<A> as, final Collection<B> bs){
assert as.size() == bs.size();
ArrayList<C> cs = new ArrayList<C>(as.size());
Iterator<A> aiter = as.iterator();
Iterator<B> biter = bs.iterator();
while(aiter.hasNext() && biter.hasNext())
cs.add(fn.apply(aiter.next(), biter.next()));
return cs;
}
/**
* Same as map, but does not retain results.
*/
public static <A> void each(Map1<A,?> fn, final Collection<A> xs){
for(A x : xs) fn.apply(x);
}
/**
* @return [x | x <- items, take(x)]
*/
public static <A> List<A> filter(Map1<A,Boolean> take, final Collection<A> xs){
List<A> rs = new ArrayList<A>(xs.size());
for(A x : xs) if(take.apply(x)) rs.add(x);
return rs;
}
/**
* @return [x | x <- items, take(x)]
*/
public static <A> Set<A> filterSet(Map1<A,Boolean> take, final Collection<A> xs){
Set<A> rs = new HashSet<A>(xs.size());
for(A x : xs) if(take.apply(x)) rs.add(x);
return rs;
}
/**
* @return the first x in items that satisfies take(x), or null if none
*/
public static <X> X find(Map1<X,Boolean> take, final Collection<X> xs){
for(X x : xs) if(take.apply(x)) return x;
return null;
}
public static <A,B> A foldl(final Map2<A,B,A> f, A a, final Collection<B> xs){
for(B b : xs) a = f.apply(a, b);
return a;
}
/**
* @return All x : p(x) == true
*/
public static <A> boolean all(Map1<A,Boolean> p, Collection<A> xs) {
for(A x : xs) if(!p.apply(x)) return false;
return true;
}
/**
* @return Any x : p(x) == true
*/
public static <A> boolean any(Map1<A,Boolean> p, Collection<A> xs) {
for(A x : xs) if(p.apply(x)) return true;
return false;
}
public static <A> String join(final String sep, final Collection<A> xs) {
if(xs.size() == 0) return "";
if(xs.size() == 1) return xs.iterator().next().toString();
return Fp.foldl(new Fp.Map2<String, A, String>(){
public String apply(String a, A b) {
String sb = b==null? "null" : b.toString();
return a == null ? sb : a + sep + sb;
}}, null, xs);
}
public static Map2<Integer,Integer,Integer> operatorPlus = new Map2<Integer, Integer, Integer>(){
public Integer apply(Integer a, Integer b) { return (int)a + (int)b;}
};
public static int sum(Collection<Integer> xs){ return foldl(operatorPlus, 0, xs); }
public static <A> List<A> append(Collection<A> xs, Collection<A> ys) {
List<A> rs = new ArrayList<A>(xs.size() + ys.size());
rs.addAll(xs);
rs.addAll(ys);
return rs;
}
public static <A> Set<A> appendSet(Collection<A> xs, Collection<A> ys) {
Set<A> rs = new HashSet<A>(xs.size() + ys.size());
rs.addAll(xs);
rs.addAll(ys);
return rs;
}
public static <K,V> Map<K,V> litMap(K key, V value, Object... pairs){
Map ret = new HashMap(1 + pairs.length/2);
ret.put(key, value);
for(int i = 0; i < pairs.length; i += 2)
ret.put(pairs[i], pairs[i+1]);
return ret;
}
}