All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.organicdesign.fp.function.Fn1 Maven / Gradle / Ivy

// Copyright 2013-12-30 PlanBase Inc. & Glen Peterson
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.organicdesign.fp.function;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

import org.organicdesign.fp.oneOf.Option;
import org.organicdesign.fp.collections.UnmodIterable;
import org.organicdesign.fp.oneOf.Or;
import org.organicdesign.fp.xform.Transformable;
import org.organicdesign.fp.xform.Xform;

/**
 This is like Java 8's java.util.function.Function, but retrofitted to turn checked exceptions
 into unchecked ones.
 */
@FunctionalInterface
public interface Fn1 extends Function, Consumer {
    // ========================================== Static ==========================================

    /** Constant functions that take an Object and return an Object */
    enum ConstObjObj implements Fn1 {
        /** The Identity function */
        IDENTITY {
            @Override
            public Object applyEx(Object t) throws Exception {
                return t;
            }

            @SuppressWarnings({"unchecked", "TypeParameterExplicitlyExtendsObject"})
            @Override
            public  Fn1 compose(Fn1 f) {
                // Composing any function with the identity function has no effect on the original
                // function (by definition of identity) - just return it.
                return (Fn1) f;
            }
        }
    }

    /** Constant functions that take an Object and return a Boolean */
    enum ConstObjBool implements Fn1 {
        /**
         A predicate that always returns true.  Use {@link #accept()} for a type-safe version of
         this predicate.
         */
        ACCEPT {
            @Override public Boolean applyEx (Object ignored) throws Exception {
                return Boolean.TRUE;
            }
        },

        /**
         A predicate that always returns false. Use {@link #reject()} for a type-safe version of
         this predicate.
         */
        REJECT {
            @Override public Boolean applyEx (Object ignored) throws Exception {
                return Boolean.FALSE;
            }
        }
    }

    @SuppressWarnings("unchecked")
    static  Fn1 identity() { return (Fn1) ConstObjObj.IDENTITY; }

    static  Fn1 or(Fn1 a, Fn1 b) {
        // Composition is not necessary in every case:
        return a == ConstObjBool.ACCEPT ? a : // If any are true, all are true.
               a == ConstObjBool.REJECT ? b : // return whatever b is.
               b == ConstObjBool.ACCEPT ? b : // If any are true, all are true.
               b == ConstObjBool.REJECT ? a : // Just amounts to if a else false.
               (S s) -> (a.apply(s) == Boolean.TRUE) || (b.apply(s) == Boolean.TRUE); // compose
    }

    static  Fn1 and(Fn1 a, Fn1 b) {
        // Composition is not necessary in every case:
        return a == ConstObjBool.ACCEPT ? b : // return whatever b is.
               a == ConstObjBool.REJECT ? a : // if any are false, all are false.
               b == ConstObjBool.ACCEPT ? a : // Just amounts to if a else false.
               b == ConstObjBool.REJECT ? b : // If any are false, all are false.
               (S s) -> (a.apply(s) == Boolean.TRUE) && (b.apply(s) == Boolean.TRUE); // compose
    }

    static  Fn1 negate(Fn1 a) {
        return a == ConstObjBool.ACCEPT ? reject() :
               a == ConstObjBool.REJECT ? accept() :
               (S s) -> (a.apply(s) == Boolean.TRUE) ? Boolean.FALSE : Boolean.TRUE;
    }

    /** Returns a type-safe version of the ConstObjBool.ACCEPT predicate. */
    @SuppressWarnings("unchecked")
    static  Fn1 accept() { return (Fn1) ConstObjBool.ACCEPT; }

    /** Returns a type-safe version of the ConstObjBool.REJECT predicate. */
    @SuppressWarnings("unchecked")
    static  Fn1 reject() { return (Fn1) ConstObjBool.REJECT; }

    /**
     Composes multiple functions into a single function to potentially minimize trips through
     the source data.  The resultant function will loop through the functions for each item in the
     source.  For a few functions and many source items, that takes less memory.  Considers no
     function to mean the IDENTITY function.  This decision is based on the way filters work and
     may or may not prove useful in practice.  Please use the identity()/IDENTITY
     sentinel value in this abstract class since function comparison is done by reference.

     LIMITATION: You could have a function that maps from T to U then the next from U to V, the
     next from V to W and so on.  So long as the output type of one matches up to the input type of
     the next, you're golden.  But type safety curls up and dies when you try to detect the
     IDENTITY function at some point in the chain.

     For arbitrary chaining, it's best to roll your own.  The following example shows how simple it
     is to chain two functions with an intermediate type into a single composite function:

     

     public static <A,B,C> Fn1<A,C> chain2(final Fn1<A,B> f1,
                                                             final Fn1<B,C> f2) {
         return new Fn1<A,C>() {
             @Override
             public C applyEx(A a) throws Exception {
                 return f2.applyEx(f1.applyEx(a));
             }
         };
     }
Even with 2 arguments, there are several signatures that would work: imagine where A=B, B=C, or A=C. I just don't see the value to providing a bunch of chain2(), chain3() etc. functions that will ultimately not be type-safe and cannot perform optimizations for you, when you can roll your own type safe versions as you need them. Only the simplest case seems worth providing, along the lines of the and() helper function in Filter() @param in the functions to applyEx in order. Nulls and IDENTITY functions are ignored. No functions means IDENTITY. @param the type of object to chain functions on @return a function which applies all the given functions in order. */ static Fn1 compose(Iterable> in) { if (in == null) { return identity(); } final List> out = new ArrayList<>(); for (Fn1 f : in) { if ((f == null) || (f == ConstObjObj.IDENTITY)) { continue; } out.add(f); } if (out.size() < 1) { return identity(); // No functions means to return the original item } else if (out.size() == 1) { return out.get(0); } else { return v -> { V ret = v; for (Fn1 f : out) { ret = f.applyEx(ret); } return ret; }; } } /** Composes multiple predicates into a single predicate to potentially minimize trips through the source data. The resultant predicate will loop through the predicates for each item in the source, but for few predicates and many source items, that takes less memory. Considers no predicate to mean "accept all." Use only accept()/ACCEPT and reject()/REJECT since function comparison is done by reference. @param in the predicates to test in order. Nulls and ACCEPT predicates are ignored. Any REJECT predicate will cause this entire method to return a single REJECT predicate. No predicates means ACCEPT. @param the type of object to predicate on. @return a predicate which returns true if all input predicates return true, false otherwise. */ static Fn1 and(Iterable> in) { if (in == null) { return accept(); } Transformable> v = (in instanceof UnmodIterable) ? (UnmodIterable>) in : Xform.of(in); Or,Fn1> ret = v.filter(p -> (p != null) && (p != ConstObjBool.ACCEPT)) .foldUntil(accept(), (accum, p) -> (p == reject()) ? p : null, Fn1::and); // (accum, p) -> and(accum, p) // We don't care whether it returns early or not. Just return whatever is in the or. return ret.match(g -> g, b -> b); } /** Composes multiple predicates into a single predicate to potentially minimize trips through the source data. The resultant predicate will loop through the predicates for each item in the source, but for few predicates and many source items, that takes less memory. Considers no predicate to mean "reject all." Use only accept()/ConstObjBool.ACCEPT and ConstObjBool.REJECT since function comparison is done by reference. @param in the predicates to test in order. Nulls and ConstObjBool.REJECT predicates are ignored. Any ACCEPT predicate will cause this entire method to return the ACCEPT predicate. No predicates means ConstObjBool.REJECT. @param the type of object to predicate on. @return a predicate which returns true if any of the input predicates return true, false otherwise. */ static Fn1 or(Iterable> in) { if (in == null) { return reject(); } Transformable> v = (in instanceof UnmodIterable) ? (UnmodIterable>) in : Xform.of(in); Or,Fn1> ret = v.filter(p -> (p != null) && (p != ConstObjBool.REJECT)) .foldUntil(reject(), (accum, p) -> (p == ConstObjBool.ACCEPT) ? p : null, Fn1::or); // (accum, p) -> or(accum, p) // We don't care whether it returns early or not. Just return whatever is in the or. return ret.match(g -> g, b -> b); } enum BooleanCombiner { AND { @Override public Fn1 combine(Iterable> in) { return and(in); } }, OR { @Override public Fn1 combine(Iterable> in) { return or(in); } }; public abstract Fn1 combine(Iterable> in); } /** Use only on pure functions with no side effects. Wrap an expensive function with this and for each input value, the output will only be computed once. Subsequent calls with the same input will return identical output very quickly. Please note that the return values from f need to implement equals() and hashCode() correctly for this to work correctly and quickly. */ static Fn1 memoize(Fn1 f) { return new Fn1() { private final Map> memo = new HashMap<>(); @Override public synchronized B applyEx(A a) throws Exception { Option val = memo.get(a); if ( (val != null) && val.isSome() ) { return val.get(); } B ret = f.apply(a); memo.put(a, Option.some(ret)); return ret; } }; } // ========================================= Instance ========================================= /** Implement this one method and you don't have to worry about checked exceptions. */ U applyEx(T t) throws Exception; /** Call this convenience method so that you don't have to worry about checked exceptions. */ @Override default U apply(T t) { try { return applyEx(t); } catch (RuntimeException re) { throw re; } catch (Exception e) { throw new RuntimeException(e); } } /** For compatibility with java.util.function.Consumer. Just a wrapper around apply(). */ @Override default void accept(T t) { apply(t); } @SuppressWarnings("unchecked") default Fn1 compose(final Fn1 f) { if (f == ConstObjObj.IDENTITY) { // This violates type safety, but makes sense - composing any function with the // identity function should return the original function unchanged. If you mess up the // types, then that's your problem. With generics and type erasure this may be the // best you can do. return (Fn1) this; } final Fn1 parent = this; return s -> parent.applyEx(f.applyEx(s)); } }