@function __reduce(
  $collection,
  $iteratee: "__identity",
  $accumulator: null,
  $this-arg: null
) {
  $function: if(
    __is-list($collection) or __is-string($collection),
    "__list-reduce",
    "__base-reduce"
  );

  @return __exec(
    $function,
    $collection,
    __get-callback($iteratee, $this-arg, 4),
    $accumulator,
    ($accumulator == null and $this-arg == null),
    __base-each
  );
}

@function __reduce-right(
  $collection,
  $iteratee: "__identity",
  $accumulator: null,
  $this-arg: null
) {
  $function: if(
    __is-list($collection) or __is-string($collection),
    "__list-reduce-right",
    "__base-reduce"
  );

  @return __exec(
    $function,
    $collection,
    __get-callback($iteratee, $this-arg, 4),
    $accumulator,
    ($accumulator == null and $this-arg == null),
    "__base-each-right"
  );
}

@function __list-reduce(
  $list,
  $iteratee,
  $accumulator: null,
  $init-from-list: true,
  $args...
) {
  $list: __to-list($list);
  $index: 1;
  $length: length($list);

  @if ($init-from-list and ($length > 0)) {
    $accumulator: nth($list, $index);
    $index: $index + 1;
  }

  @while ($index <= $length) {
    $value: nth($list, $index);
    $accumulator: __exec($iteratee, $accumulator, $value, $index, $list);

    $index: $index + 1;
  }

  @return $accumulator;
}

@function __list-reduce-right(
  $list,
  $iteratee,
  $accumulator: null,
  $init-from-list: true,
  $args...
) {
  $list: __to-list($list);
  $list: __list-reverse($list);

  @return __list-reduce($list, $iteratee, $accumulator, $init-from-list);
}

@function __base-reduce-iteratee($value, $index, $collection: null) {
  $iteratee: __this("iteratee");
  $accumulator: __this("accumulator");
  $init-from-collection: __this("init-from-collection");

  @if ($init-from-collection) {
    $_: __this("init-from-collection", false);
    $accumulator: $value;
  } @else {
    $accumulator: __exec($iteratee, $accumulator, $value, $index, $collection);
  }

  $_: __this("accumulator", $accumulator);

  @return true;
}

@function __base-reduce(
  $collection,
  $iteratee,
  $accumulator,
  $init-from-collection: false,
  $each-function: null
) {
  $_: __scope(
    (
      "iteratee": $iteratee,
      "accumulator": $accumulator,
      "init-from-collection": $init-from-collection
    )
  );
  $iteratee: __bind("__base-reduce-iteratee");
  $_: __exec($each-function, $collection, $iteratee);
  $accumulator: __this("accumulator");
  $_: __scope(false);

  @return $accumulator;
}

/// Reduces `$collection` to a value which is the accumulated result of running
/// each element in `$collection` through `$iteratee`, where each successive
/// invocation is supplied the return value of the previous. If `$accumulator`
/// is not provided the first element of `$collection` is used as the initial
/// value. The `$iteratee` is bound to `$this-arg`and invoked with four arguments;
/// (accumulator, value, index|key, collection).
///
/// @access public
/// @group Collection
/// @param {List|Map|string} $collection The collection to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $accumulator [null] The initial value.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {*} Returns the accumulated value.
/// @example scss
/// // todo

@function _reduce($args...) {
  @return call(get-function("__reduce"), $args...);
}

/// @alias _reduce

@function _foldl($args...) {
  @return call(get-function("__reduce"), $args...);
}

/// @alias _reduce

@function _inject($args...) {
  @return call(get-function("__reduce"), $args...);
}

/// This method is like `_reduce` except that it iterates over elements of
/// `$collection` from right to left.
///
///
/// @access public
/// @group Collection
/// @param {List|Map|string} $collection The collection to iterate over.
/// @param {Function} $iteratee [_identity] - The function invoked per iteration.
/// @param {*} $accumulator [null] - The initial value.
/// @param {*} $this-arg [null] - The `_this` binding of `$iteratee`.
/// @returns {*} Returns the accumulated value.
/// @example scss
/// // todo

@function _reduce-right($args...) {
  @return call(get-function("__reduce-right"), $args...);
}

/// @alias _reduce-right

@function _foldr($args...) {
  @return call(get-function("__reduce-right"), $args...);
}
