/*!
 * Copyright (c) 2017-2018 by The Funfix Project Developers.
 * Some rights reserved.
 *
 * 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.
 */

/* @flow */

import type { HK, Monad } from "funland"
import { Either } from "funfix-core"

declare export class Eval<+A> {
  get(): A;

  map<B>(f: (a: A) => B): Eval<B>;
  flatMap<B>(f: (a: A) => Eval<B>): Eval<B>;
  chain<B>(f: (a: A) => Eval<B>): Eval<B>;
  ap<B>(ff: Eval<(a: A) => B>): Eval<B>;
  memoize(): Eval<A>;
  forEachL(cb: (a: A) => void): Eval<void>;
  forEach(cb: (a: A) => void): void;

  static +_Class: Eval<any>;

  static of<A>(thunk: () => A): Eval<A>;
  static pure<A>(value: A): Eval<A>;
  static now<A>(value: A): Eval<A>;
  static unit(): Eval<void>;
  static always<A>(thunk: () => A): Eval<A>;
  static once<A>(thunk: () => A): Eval<A>;
  static suspend<A>(thunk: () => Eval<A>): Eval<A>;
  static defer<A>(thunk: () => Eval<A>): Eval<A>;
  static tailRecM<A, B>(a: A, f: (a: A) => Eval<Either<A, B>>): Eval<B>;

  static sequence<A>(list: Iterable<Eval<A>>): Eval<A[]>;
  static map2<A1, A2, R>(fa1: Eval<A1>, fa2: Eval<A2>, f: (a1: A1, a2: A2) => R): Eval<R>;
  static map3<A1, A2, A3, R>(fa1: Eval<A1>, fa2: Eval<A2>, fa3: Eval<A3>, f: (a1: A1, a2: A2, a3: A3) => R): Eval<R>;
  static map4<A1, A2, A3, A4, R>(fa1: Eval<A1>, fa2: Eval<A2>, fa3: Eval<A3>, fa4: Eval<A4>, f: (a1: A1, a2: A2, a3: A3, a4: A4) => R): Eval<R>;
  static map5<A1, A2, A3, A4, A5, R>(fa1: Eval<A1>, fa2: Eval<A2>, fa3: Eval<A3>, fa4: Eval<A4>, fa5: Eval<A5>, f: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => R): Eval<R>;
  static map6<A1, A2, A3, A4, A5, A6, R>(fa1: Eval<A1>, fa2: Eval<A2>, fa3: Eval<A3>, fa4: Eval<A4>, fa5: Eval<A5>, fa6: Eval<A6>, f: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => R): Eval<R>;
}

export type EvalURI = <U, L, A>(x: [U, L, A]) => Eval<A>;
export type EvalK<A> = HK<EvalURI, A>;

export type EvalTypes = Monad<EvalURI>;
declare export var EvalModule: EvalTypes;
