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

com.shapesecurity.functional.data.Maybe Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright 2014 Shape Security, Inc.
 *
 * 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 com.shapesecurity.functional.data;

import com.shapesecurity.functional.F;
import com.shapesecurity.functional.Thunk;
import com.shapesecurity.functional.Unit;


import java.util.Optional;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public abstract class Maybe {
  private final static Maybe NOTHING = new Nothing<>();

  // class local
  private Maybe() {
  }

  @SuppressWarnings("unchecked")
  @NotNull
  public static  Maybe nothing() {
    return (Maybe) NOTHING;
  }

  @NotNull
  public static  Maybe just(@NotNull A a) {
    return new Just<>(a);
  }

  @NotNull
  public static  Maybe fromNullable(@Nullable A a) {
    if (a == null) {
      return nothing();
    }
    return just(a);
  }

  @Nullable
  public abstract A toNullable();

  @NotNull
  public static  ImmutableList catMaybes(@NotNull ImmutableList> l) {
    return l.foldRight((a, b) -> a.maybe(b, c -> ImmutableList.cons(c, b)), ImmutableList.nil());
  }

  @NotNull
  public static  ImmutableList mapMaybe(@NotNull final F f, @NotNull ImmutableList> l) {
    return l.foldRight((a, b) -> a.maybe(b, v -> ImmutableList.cons(f.apply(v), b)), ImmutableList.nil());
  }

  @SuppressWarnings("BooleanParameter")
  @NotNull
  public static  Maybe iff(boolean test, @NotNull A a) {
    if (test) {
      return just(a);
    }
    return nothing();
  }

  public abstract boolean eq(@NotNull Maybe maybe);

  @SuppressWarnings("unchecked")
  @Override
  public final boolean equals(Object obj) {
    return obj == this || obj instanceof Maybe && this.eq((Maybe) obj);
  }

  @Override
  public abstract int hashCode();

  @NotNull
  public abstract A just() throws NullPointerException;

  @NotNull
  public abstract  B maybe(@NotNull B def, @NotNull F f);

  public final void foreach(@NotNull F f) {
    map(f);
  }

  public abstract boolean isJust();

  public final boolean isNothing() {
    return !this.isJust();
  }

  @NotNull
  public abstract ImmutableList toList();

  @NotNull
  public abstract A orJust(@NotNull A a);

  @NotNull
  public abstract A orJustLazy(@NotNull Thunk a);

  @NotNull
  public abstract  Maybe map(@NotNull F f);

  @NotNull
  public final  Maybe bind(@NotNull F> f) {
    return this.flatMap(f);
  }

  @NotNull
  public abstract  Maybe flatMap(@NotNull F> f);

  private static class Just extends Maybe {
    @NotNull
    private final A value;
    private Thunk hashCodeThunk = Thunk.from(this::calcHashCode);

    private Just(@NotNull A value) {
      super();
      this.value = value;
    }

    private int calcHashCode() {
      return HashCodeBuilder.put(this.value.hashCode(), "Just");
    }

    @Nullable
    @Override
    public A toNullable() {
      return value;
    }

    @Override
    public boolean eq(@NotNull Maybe maybe) {
      return maybe instanceof Just && maybe.just().equals(this.value);
    }

    @Override
    public int hashCode() {
      return this.hashCodeThunk.get();
    }

    @NotNull
    @Override
    public A just() throws NullPointerException {
      return this.value;
    }

    @NotNull
    @Override
    public  B maybe(@NotNull B def, @NotNull F f) {
      return f.apply(this.value);
    }

    @Override
    public boolean isJust() {
      return true;
    }

    @NotNull
    @Override
    public ImmutableList toList() {
      return ImmutableList.cons(this.value, ImmutableList.nil());
    }

    @NotNull
    @Override
    public A orJust(@NotNull A a) {
      return this.value;
    }

    @NotNull
    @Override
    public A orJustLazy(@NotNull Thunk a) {
      return this.value;
    }

    @NotNull
    @Override
    public  Maybe map(@NotNull F f) {
      return Maybe.just(f.apply(this.value));
    }

    @NotNull
    @Override
    public  Maybe flatMap(@NotNull F> f) {
      return f.apply(this.value);
    }
  }

  private static class Nothing extends Maybe {
    private final static int HASH_CODE = HashCodeBuilder.put(HashCodeBuilder.init(), "Nothing");

    @Nullable
    @Override
    public A toNullable() {
      return null;
    }

    @Override
    public boolean eq(@NotNull Maybe maybe) {
      return maybe == this;
    }

    @Override
    public final int hashCode() {
      return HASH_CODE;
    }

    @NotNull
    @Override
    public A just() throws NullPointerException {
      throw new NullPointerException("Maybe.just failed");
    }

    @NotNull
    @Override
    public  B maybe(@NotNull B def, @NotNull F f) {
      return def;
    }

    @Override
    public boolean isJust() {
      return false;
    }

    @NotNull
    @Override
    public ImmutableList toList() {
      return ImmutableList.nil();
    }

    @NotNull
    @Override
    public A orJust(@NotNull A a) {
      return a;
    }

    @NotNull
    @Override
    public A orJustLazy(@NotNull Thunk a) {
      return a.get();
    }

    @SuppressWarnings("unchecked")
    @NotNull
    @Override
    public  Maybe map(@NotNull F f) {
      return (Maybe) NOTHING;
    }

    @SuppressWarnings("unchecked")
    @NotNull
    @Override
    public  Maybe flatMap(@NotNull F> f) {
      return (Maybe) NOTHING;
    }
  }
}