from typing import Callable


def filter(f: Callable, collection):
    """Returns a new collection containing elements where `f` returns ``True``.

    Examples
    --------
    .. doctest::

        >>> a = [1, 2, 3, 4]
        >>> s = {'Alice', 'Bob', 'Charlie'}

        >>> hl.eval_expr(hl.filter(lambda x: x % 2 == 0, a))
        [2, 4]

        >>> hl.eval_expr(hl.filter(lambda x: ~(x[-1] == 'e'), s))
        {'Bob'}

    Notes
    -----
    Returns a same-type expression; evaluated on a :class:`.SetExpression`, returns a
    :class:`.SetExpression`. Evaluated on an :class:`.ArrayExpression`,
    returns an :class:`.ArrayExpression`.

    Parameters
    ----------
    f : function ( (arg) -> :class:`.BooleanExpression`)
        Function to evaluate for each element of the collection. Must return a
        :class:`.BooleanExpression`.
    collection : :class:`.ArrayExpression` or :class:`.SetExpression`.
        Array or set expression to filter.

    Returns
    -------
    :class:`.ArrayExpression` or :class:`.SetExpression`
        Expression of the same type as `collection`.
    """
    return collection._bin_lambda_method("filter", f, collection.dtype.element_type, lambda _: collection.dtype)