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

me.shaftesbury.utils.functional.Option Maven / Gradle / Ivy

There is a newer version: 1.17
Show newest version
package me.shaftesbury.utils.functional;

/**
 * Option is an implementation of the option monad.
 * See http://en.wikipedia.org/wiki/Option_type
 * {@see http://en.wikipedia.org/wiki/Monad_(functional_programming)}
 */
public final class Option
{
    private final T _t;
    private Option()
    {
        _t=null;
    }
    private Option(final T t)
    {
        _t = t;
    }

    /**
     * If this Option has a value then return it otherwise throw an exception. It is not intended that this method is called
     * without a prior call to {@link #isSome()}
     * @throws me.shaftesbury.utils.functional.OptionNoValueAccessException
     * @return the underlying value of the Option
     */
    public T Some()
    {
        if(_t!=null) return _t;
        else throw new OptionNoValueAccessException();
    }

    /**
     * Factory method similar to {@link #toOption(Object)}. The difference between them is that this method throws an exception
     * if a null is passed as the argument. {@link #toOption(Object)} returns a None.
     * @param t the object which is to be wrapped in an Option
     * @return the Option containing t
     * @throws me.shaftesbury.utils.functional.OptionNoValueAccessException
     */
    public static Option Some(final U t)
    {
        if(t==null) throw new OptionNoValueAccessException();
        return toOption(t);
    }

    /**
     * Factory method which returns an Option with no underlying value, ie a None
     * @param  the type of the underlying data, had there been any.
     * @return the Option None
     */
    public static Option None()
    {
        return new Option();
    }

    /**
     * Predicate. Does this Option object have a value?
     * @return true if this Option has a value, false otherwise
     */
    public boolean isSome()
    {
        return _t!=null;
    }

    /**
     * Predicate. Does this Option have a value?
     * @return true if this Option does not have a value, false otherwise.
     */
    public boolean isNone()
    {
        return _t==null;
    }

    /**
     * Equality operator. If the two Options are of the same type, they are both {@link #isSome()} and {@link #Some()} are
     * both equal then the two Options are considered to be equal. If one or both is {@link #None()}, o is not an
     * Option, o is an Option of another type or o is an Option of the same type but having a non-equal
     * underlying value (as determined using the underlying value's {@link #equals(Object)} function) then the two are not
     * considered equal.
     * @param o the Option which we wish to compare for equality with this.
     * @return true if they compare equally, false otherwise
     */
    public boolean equals(final Object o)
    {
        if(o instanceof Option)
        {
            final Option other = (Option)o;
            try
            {
                return isSome()==other.isSome() && Some().equals(other.Some());
            }
            catch(final OptionNoValueAccessException ex)
            {
                return isNone() && other.isNone(); // every None is considered to be the same
            }
        }
        else return false;
    }

    /**
     * Return the hashCode of this Option
     * @return an integer hashcode
     */
    public int hashCode()
    {
        return isNone() ? 0 : 31 * _t.hashCode();
    }

    /**
     * Return a rendering of this Option as a String. That is, "Option( value.toString() )" or "None"
     * @return the string representation of the Option.
     */
    public String toString()
    {
        return isSome()
                ? "Option( "+ Some().toString() + " )"
                : "None";
    }

    /**
     * Factory method similar to {@link #Some(Object)}. The difference between them is that this method returns a None
     * if a null is passed as the argument. {@link #toOption(Object)} throws an exception.
     * @param t the object which is to be wrapped in an Option
     * @return the Option containing t
     */
    public static Option toOption(U t)
    {
        return new Option(t);
    }

    /**
     * Apply a function to the underlying data if {@link #isSome()} and return the result otherwise return {@link #None()}
     * {@see http://en.wikipedia.org/wiki/Monad_(functional_programming)}
     * @param f the function to be bound
     * @param  the type of the resulting Option type
     * @return an Option containing either {@link #None()} or the result of the function f
     */
    public Option bind(final Func> f)
    {
        if(isSome()) return f.apply(Some());
        else return Option.None();
    }

    /**
     * Given two monadic Options apply the supplied binary function to them if they are both {@link #isSome()} and return
     * a wrapped Option containing the result or {@link #None()}.
     * @param f the binary function to be lifted
     * @param o1 the first Option to be passed to the lift function f
     * @param o2 the second Option to be passed to the lift function f
     * @param  the type of the first Option
     * @param  the type of the second Option
     * @param  the type of the resulting Option
     * @return an Option containing the result of the lifted function as applied to o1 and o2 or {@link #None()}
     */
    public static Optionlift(final Func2 f, final Option o1, final Option o2)
    {
        if(o1.isSome() && o2.isSome()) return toOption(f.apply(o1.Some(),o2.Some()));
        else return None();
    }
}