Skip to content

[Question] How to express a recursive type? #2777

@someniatko

Description

@someniatko

Hello there!

I try to type a flatten() function from the nikic\iter package. I have no ideas how to do this using current psalm. I would appreciate any help. I have copied the function definition below:

source:
/**
 * Takes an iterable containing any amount of nested iterables and returns
 * a flat iterable with just the values.
 *
 * The $level argument allows to limit flattening to a certain number of levels.
 *
 * Examples:
 *
 *      iter\flatten([1, [2, [3, 4]], [5]])
 *      => iter(1, 2, 3, 4, 5)
 *      iter\flatten([1, [2, [3, 4]], [5]], 1)
 *      => iter(1, 2, [3, 4], 5)
 *
 * @param iterable $iterable Iterable to flatten
 * @param int $levels Number of levels to flatten
 *
 * @return \Iterator
 */
function flatten(iterable $iterable, $levels = INF): \Iterator {
    if ($levels < 0) {
        throw new \InvalidArgumentException(
            'Number of levels must be non-negative'
        );
    }

    if ($levels === 0) {
        // Flatten zero levels == do nothing
        yield from $iterable;
    } else if ($levels === 1) {
        // Optimized implementation for flattening one level
        foreach ($iterable as $key => $value) {
            if (isIterable($value)) {
                yield from $value;
            } else {
                yield $key => $value;
            }
        }
    } else {
        // Otherwise flatten recursively
        foreach ($iterable as $key => $value) {
            if (isIterable($value)) {
                yield from flatten($value, $levels - 1);
            } else {
                yield $key => $value;
            }
        }
    }
}

This is what I did so far:

First attempt:
/**
 * FIXME: probably not solvable without recursive types like in TypeScript.
 * Types are only describen for given nesting level of 1.
 *
 * @template T
 *
 * @param iterable<T|iterable<T>> $iterable
 * @param int $levels
 * @return \Iterator<T>
 */
function flatten(iterable $iterable, $levels = INF): \Iterator {}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions