One of the motivating examples for withoutActuallyEscaping looks like the following. This predictably doesn’t work because "use of non-escaping parameter 'predicate' may allow it to escape"
There are two overloads of filter() available on ‘array.lazy’; the version that takes an escaping closure and returns a LazyFilterCollection, and the version that takes a non-escaping closure and returns [Int].
In the first example, we pick the LazyFilterCollection-returning overload, because the literal closure { predicate($0) } can be coerced to both an escaping or a non-escaping closure type, and in the absence of additional constraints we go with the overload from a concrete type over an overload in a protocol extension. After the overload has been picked we validate the body of the closure, and notice that it is invalid because whole the closure is already known to be @escaping, it references the non-@escaping ‘predicate’.
In the second example, ‘predicate’ is known to be non-@escaping, which rules out the first overload completely, so we go with the second overload and perform a non-lazy filter.
I would argue this is somewhat confusing, but it might be difficult to change the overload resolution rules in a way where the first overload is always chosen.
Slava
···
On Mar 26, 2017, at 12:13 AM, Ray Fix via swift-users <swift-users@swift.org> wrote:
Hi,
One of the motivating examples for withoutActuallyEscaping looks like the following. This predictably doesn’t work because "use of non-escaping parameter 'predicate' may allow it to escape"
Here’s another fun one: the following type checks despite the literal closure; we’ve constrained the return type of filter() to [Int], and not LazyFilterCollection<Int>, so again the type checker picks the overload that takes the non-escaping closure:
On Mar 26, 2017, at 1:14 AM, Slava Pestov via swift-users <swift-users@swift.org> wrote:
Hi Ray,
There are two overloads of filter() available on ‘array.lazy’; the version that takes an escaping closure and returns a LazyFilterCollection, and the version that takes a non-escaping closure and returns [Int].
In the first example, we pick the LazyFilterCollection-returning overload, because the literal closure { predicate($0) } can be coerced to both an escaping or a non-escaping closure type, and in the absence of additional constraints we go with the overload from a concrete type over an overload in a protocol extension. After the overload has been picked we validate the body of the closure, and notice that it is invalid because whole the closure is already known to be @escaping, it references the non-@escaping ‘predicate’.
In the second example, ‘predicate’ is known to be non-@escaping, which rules out the first overload completely, so we go with the second overload and perform a non-lazy filter.
I would argue this is somewhat confusing, but it might be difficult to change the overload resolution rules in a way where the first overload is always chosen.
Slava
On Mar 26, 2017, at 12:13 AM, Ray Fix via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
Hi,
One of the motivating examples for withoutActuallyEscaping looks like the following. This predictably doesn’t work because "use of non-escaping parameter 'predicate' may allow it to escape"
If you understand why this is so, it would be very helpful.
Thank you and best wishes,
Ray
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org> https://lists.swift.org/mailman/listinfo/swift-users
It seems like we could just not take escaping-ness into account at all, and only diagnose if it mismatches. We'd have to decide if we want that behavior, though.
Jordan
···
On Mar 26, 2017, at 01:14, Slava Pestov via swift-users <swift-users@swift.org> wrote:
Hi Ray,
There are two overloads of filter() available on ‘array.lazy’; the version that takes an escaping closure and returns a LazyFilterCollection, and the version that takes a non-escaping closure and returns [Int].
In the first example, we pick the LazyFilterCollection-returning overload, because the literal closure { predicate($0) } can be coerced to both an escaping or a non-escaping closure type, and in the absence of additional constraints we go with the overload from a concrete type over an overload in a protocol extension. After the overload has been picked we validate the body of the closure, and notice that it is invalid because whole the closure is already known to be @escaping, it references the non-@escaping ‘predicate’.
In the second example, ‘predicate’ is known to be non-@escaping, which rules out the first overload completely, so we go with the second overload and perform a non-lazy filter.
I would argue this is somewhat confusing, but it might be difficult to change the overload resolution rules in a way where the first overload is always chosen.
Wow. Thanks for the explanation! I didn’t realize I was toggling between different overloads but that makes sense now.
Thanks again,
Ray
PS: It is sort of a strange example in the first place (being lazy and then immediately eager.) I don’t think it is a use case that is compelling enough to consider overload resolution rules over.
···
On Mar 26, 2017, at 1:21 AM, Slava Pestov <spestov@apple.com> wrote:
Here’s another fun one: the following type checks despite the literal closure; we’ve constrained the return type of filter() to [Int], and not LazyFilterCollection<Int>, so again the type checker picks the overload that takes the non-escaping closure:
On Mar 26, 2017, at 1:14 AM, Slava Pestov via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
Hi Ray,
There are two overloads of filter() available on ‘array.lazy’; the version that takes an escaping closure and returns a LazyFilterCollection, and the version that takes a non-escaping closure and returns [Int].
In the first example, we pick the LazyFilterCollection-returning overload, because the literal closure { predicate($0) } can be coerced to both an escaping or a non-escaping closure type, and in the absence of additional constraints we go with the overload from a concrete type over an overload in a protocol extension. After the overload has been picked we validate the body of the closure, and notice that it is invalid because whole the closure is already known to be @escaping, it references the non-@escaping ‘predicate’.
In the second example, ‘predicate’ is known to be non-@escaping, which rules out the first overload completely, so we go with the second overload and perform a non-lazy filter.
I would argue this is somewhat confusing, but it might be difficult to change the overload resolution rules in a way where the first overload is always chosen.
Slava
On Mar 26, 2017, at 12:13 AM, Ray Fix via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:
Hi,
One of the motivating examples for withoutActuallyEscaping looks like the following. This predictably doesn’t work because "use of non-escaping parameter 'predicate' may allow it to escape"
If you understand why this is so, it would be very helpful.
Thank you and best wishes,
Ray
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org> https://lists.swift.org/mailman/listinfo/swift-users
on Mon Mar 27 2017, Jordan Rose <jordan_rose-AT-apple.com> wrote:
On Mar 26, 2017, at 01:14, Slava Pestov via swift-users <swift-users@swift.org> wrote:
Hi Ray,
There are two overloads of filter() available on ‘array.lazy’; the
version that takes an escaping closure and returns a
LazyFilterCollection, and the version that takes a non-escaping
closure and returns [Int].
In the first example, we pick the LazyFilterCollection-returning
overload, because the literal closure { predicate($0) } can be
coerced to both an escaping or a non-escaping closure type, and in
the absence of additional constraints we go with the overload from a
concrete type over an overload in a protocol extension. After the
overload has been picked we validate the body of the closure, and
notice that it is invalid because whole the closure is already known
to be @escaping, it references the non-@escaping ‘predicate’.
In the second example, ‘predicate’ is known to be non-@escaping,
which rules out the first overload completely, so we go with the
second overload and perform a non-lazy filter.
I would argue this is somewhat confusing, but it might be difficult
to change the overload resolution rules in a way where the first
overload is always chosen.
It seems like we could just not take escaping-ness into account at
all, and only diagnose if it mismatches. We'd have to decide if we
want that behavior, though.
Yes, you won't get the lazy behavior when you pass a throwing closure to filter(_:)—none of the lazy versions accept throwing closures, so it falls back to the original Sequence version. With a throwing closure, the entire filter operation has to happen right away in order to know whether it throws or not, so it can't be lazy.