<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def approx_eq(val: Any, other: Any, *, atol: Union[int, float] = 1e-8) -> bool: """Approximately compares two objects. If `val` implements SupportsApproxEquality protocol then it is invoked and takes precedence over all other checks: - For primitive numeric types `int` and `float` approximate equality is delegated to math.isclose(). - For complex primitive type the real and imaginary parts are treated independently and compared using math.isclose(). - For `val` and `other` both iterable of the same length, consecutive elements are compared recursively. Types of `val` and `other` does not necessarily needs to match each other. They just need to be iterable and have the same structure. Args: val: Source object for approximate comparison. other: Target object for approximate comparison. atol: The minimum absolute tolerance. See np.isclose() documentation for details. Defaults to 1e-8 which matches np.isclose() default absolute tolerance. Returns: True if objects are approximately equal, False otherwise. """


    # Check if val defines approximate equality via _approx_eq_. This takes
    # precedence over all other overloads.
    approx_eq_getter = getattr(val, '_approx_eq_', None)
    if approx_eq_getter is not None:
        result = approx_eq_getter(other, atol)
        if result is not NotImplemented:
            return result

    # The same for other to make approx_eq symmetric.
    other_approx_eq_getter = getattr(other, '_approx_eq_', None)
    if other_approx_eq_getter is not None:
        result = other_approx_eq_getter(val, atol)
        if result is not NotImplemented:
            return result

    # Compare primitive types directly.
    if isinstance(val, (int, float)):
        if not isinstance(other, (int, float)):
            return False
        return _isclose(val, other, atol=atol)

    if isinstance(val, complex):
        if not isinstance(other, complex):
            return False
        return _isclose(val, other, atol=atol)

    # Try to compare source and target recursively, assuming they're iterable.
    result = _approx_eq_iterables(val, other, atol=atol)

    # Fallback to __eq__() when anything else fails.
    if result is NotImplemented:
        return val == other
    return result