<commit_before>package thx.promise;

import haxe.ds.Option;
using thx.core.Arrays;
import thx.core.Error;
import thx.core.Nil;
using thx.core.Options;
import thx.core.Tuple;

class Future<T> {
  public static function all<T>(arr : Array<Future<T>>) : Future<Array<T>>
    return Future.create(function(callback) {
      var results = [],
          counter = 0;
      arr.mapi(function(p, i) {
        p.then(function(value) {
          results[i] = value;
          counter++;
          if(counter == arr.length)
            callback(results);
        });
      });
    });

  public static function create<T>(handler : (T -> Void) -> Void) {
    var future = new Future<T>();
    handler(future.setState);
    return future;
  }

  public static function flatMap<T>(future : Future<Future<T>>) : Future<T> {
    var nfuture = new Future<T>();
    future.then(function(f) f.then(nfuture.setState));
    return nfuture;
  }

  public static function value<T>(v : T)
    return create(function(callback) callback(v));

  var handlers : Array<T -> Void>;
  public var state(default, null) : Option<T>;
  private function new() {
    handlers = [];
    state = None;
  }

#if (js || flash || java)
  inline public function delay(?delayms : Int) {
    if(null == delayms)
      return mapFuture(function(value) return Timer.immediateValue(value));
    else
      return mapFuture(function(value) return Timer.delayValue(value, delayms));
  }
#end

  inline public function hasValue()
    return state.toBool();

  public function map<TOut>(handler : T -> TOut) : Future<TOut>
    return Future.create(function(callback)
      then(function(value)
        callback(handler(value))));

  public function mapAsync<TOut>(handler : T -> (TOut -> Void) -> Void) : Future<TOut>
    return Future.create(function(callback)
      then(function(result : T )
        handler(result, callback)));

  inline public function mapFuture<TOut>(handler : T -> Future<TOut>) : Future<TOut>
    return flatMap(map(handler));

  public function then(handler : T -> Void) {
    handlers.push(handler);
    update();
    return this;
  }

  public function toString() return 'Future';

  function setState(newstate : T) {
    switch state {
      case None:
        state = Some(newstate);
      case Some(r):
        throw new Error('future was already "$r", can\'t apply the new state "$newstate"');
    }
    update();
    return this;
  }

  function update()
    switch state {
      case None:
      case Some(result): {
        var handler;
        while(null != (handler = handlers.shift()))
          handler(result);
      }
    };
}

class Futures {
  public static function join<T1,T2>(p1 : Future<T1>, p2 : Future<T2>) : Future<Tuple2<T1,T2>> {
    return Future.create(function(callback) {
      var counter = 0,
          v1 : Null<T1> = null,
          v2 : Null<T2> = null;

      function complete() {
        if(counter < 2)
          return;<commit_msg>more efficient loop<commit_after>package thx.promise;

import haxe.ds.Option;
using thx.core.Arrays;
import thx.core.Error;
import thx.core.Nil;
using thx.core.Options;
import thx.core.Tuple;

class Future<T> {
  public static function all<T>(arr : Array<Future<T>>) : Future<Array<T>>
    return Future.create(function(callback) {
      var results = [],
          counter = 0;
      arr.mapi(function(p, i) {
        p.then(function(value) {
          results[i] = value;
          counter++;
          if(counter == arr.length)
            callback(results);
        });
      });
    });

  public static function create<T>(handler : (T -> Void) -> Void) {
    var future = new Future<T>();
    handler(future.setState);
    return future;
  }

  public static function flatMap<T>(future : Future<Future<T>>) : Future<T> {
    var nfuture = new Future<T>();
    future.then(function(f) f.then(nfuture.setState));
    return nfuture;
  }

  public static function value<T>(v : T)
    return create(function(callback) callback(v));

  var handlers : Array<T -> Void>;
  public var state(default, null) : Option<T>;
  private function new() {
    handlers = [];
    state = None;
  }

#if (js || flash || java)
  inline public function delay(?delayms : Int) {
    if(null == delayms)
      return mapFuture(function(value) return Timer.immediateValue(value));
    else
      return mapFuture(function(value) return Timer.delayValue(value, delayms));
  }
#end

  inline public function hasValue()
    return state.toBool();

  public function map<TOut>(handler : T -> TOut) : Future<TOut>
    return Future.create(function(callback)
      then(function(value)
        callback(handler(value))));

  public function mapAsync<TOut>(handler : T -> (TOut -> Void) -> Void) : Future<TOut>
    return Future.create(function(callback)
      then(function(result : T )
        handler(result, callback)));

  inline public function mapFuture<TOut>(handler : T -> Future<TOut>) : Future<TOut>
    return flatMap(map(handler));

  public function then(handler : T -> Void) {
    handlers.push(handler);
    update();
    return this;
  }

  public function toString() return 'Future';

  function setState(newstate : T) {
    switch state {
      case None:
        state = Some(newstate);
      case Some(r):
        throw new Error('future was already "$r", can\'t apply the new state "$newstate"');
    }
    update();
    return this;
  }

  function update()
    switch state {
      case None:
      case Some(result): {
        var index = -1;
        while(++index < handlers.length)
          handlers[index](result);
        handlers = [];
      }
    };
}

class Futures {
  public static function join<T1,T2>(p1 : Future<T1>, p2 : Future<T2>) : Future<Tuple2<T1,T2>> {
    return Future.create(function(callback) {
      var counter = 0,
          v1 : Null<T1> = null,
          v2 : Null<T2> = null;

      function complete() {
        if(counter < 2)
          return;
{'fraction_non_alphanumeric': 0.13955059976347356, 'fraction_numerical': 0.005575266092245312, 'mean_word_length': 2.811976819059884, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4007328', 'n_tokens_mistral': 1875, 'n_tokens_neox': 1825, 'n_words': 460}