func updateImage() async {
let image: Image
async do { // Runs in parallel (async)
image = try async preprocessImage(downloadImage())
} catch {
image = defaultImage
}
let text: String
async do { // Runs in parallel (async)
text = try async translate(downloadText())
} catch {
text = defaultText
}
// This line is complicated! We want render not to block (async), but have to
await for image and text.
// Render does not throw because it always has valid input.
// If async were allowed to prevent blocking then await could not
async render(image: await image, text: await text)
}
Which I don't think reads as well as the Future version:
func updateImage() -> Future<Void> {
return AsynchronousFuture { _ -> Void in
let image = preprocessImage(downloadImage()) // Parallel, Futures are
queued on creation
let text = translate(downloadText()) // Parallel, Futures are queued on
creation
// Does not block (Futures are queued on creation), but has to wait for
its inputs (get).
render(image: image.get ?? defaultImage, text: text.get ?? defaultText)
}
}
In addition the async/await version does not have timeout; unlike the Future version.
Suppose that downloadImage doesn't fail, it just takes forever to download the image. The Future version will timeout automatically and the default image will be used. With async/await the code for downloadImage and downloadText will have to start timers and throw if the timers timeout. Nothing to do in the Future version, it handles timeout for you.
Neither or the above versions have cancel or control over the queue they execute on, but both would be much easier to add to the Future version, like timeout is much easier to add, since Futures support cancel and queue control directly.
-- Howard.
On 30 August 2017 at 02:45, Vladimir.S via swift-evolution <swift-evolution@swift.org > <mailto:swift-evolution@swift.org>> wrote:
On 29.08.2017 19:02, Wallacy via swift-evolution wrote:
In this example i think we lose clarity, just looking for the code we cant
know if this two line will run on parallel or not!
Also, image.get blocks the thread, in this case we need the await anyway! And
`async` can throws too... So the error handler can be pretty similar.
let image= asyncpreprocessImage(downloadImage()) // These first two lines
run in parallel and I can "see" the async keyword.
let text= asynctranslate(downloadText())
await render(image: image ?? defaultImage,text: text ?? defaultText) // No
blocking!
FWIW: I'm following the whole discussion from the start, and do support the
opinion that async/await is much clear solution that proposed Futures, especially
for beginners.
We need a low-level building blocks which can be used to implement
Futures/Promises in libraries.
Also I really like the idea of 'async' on the caller side to have code running in
parallel.
The 'async' version of func declaration is clearly saying what type it *want* to
return, and 'async' modifier just saying *how* it will/can return that
type('Image' in examples). So on both sides, on declaration and on caller side,
we are clear what types we are working with.
Future<Type> - is mixing of what is returning and how this will be returned. Code
is saying that we preprocessesImage, but actually we have Future<Image> type, no
'markers' of asynchronous code.
Also, I wonder(if I missed that in proposal/discussion, please let me know), if I
have async function like
func foo() async -> Type {}
, may I want to call it synchronously? If so, what would be a solution here? I
can think about something like 'sync' modifier on caller side:
let x = sync foo() // calling asynchronous function synchronously
I believe that is what Future.get is doing, no?
let future = ...
future.get() // blocks the execution, waits for the result.
Probably it is reasonable to allow just call foo() to get blocking result, just
like any other 'simple' blocking funcs that we call, but this can lead to
unexpected behavior as user can expect async execution.
With Futures, it seems like we can't "just" call such function and need to call
.get() later:
let future = someFuncReturnsFuture() // already returns Future<Type> type
Vladimir.
Like i said before! Today's, the proposal only lack two things over the
`Future`....
Parallel computing: Can be implemented by a third party library or a personal
one, but i don't think this is a good approach to the first version.
Coordination: This we can wait! And why? Because coordination, can be made in
different ways, maybe is more suitable to a standard library class/function,
not a language level resource.
Also, coordination cant be applied to all variants of the runtimes in the
same way! async/await as language level works just as well with GCD as with
pthreads or another API. And coordination is a compromise that we can make
after that one.
Em ter, 29 de ago de 2017 às 05:23, Howard Lovatt via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org> > <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> escreveu:
@David,
Using the `Future` library based on GCD that I have previously posted your
example would be:
let image= preprocessImage(downloadImage()) // These first two lines
run in parallel
let text= translate(downloadText())
render(image: image.get ?? defaultImage,text: text.get ?? defaultText)
The main difference, and I would argue an improvement, is that the `Future`
version handles errors.
So what advantage does async/await have over a `Future` library we can
write today?
-- Howard.
On 29 August 2017 at 15:28, David Hart via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org> > <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:
On 29 Aug 2017, at 02:22, Xiaodi Wu via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org> > <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:
On Mon, Aug 28, 2017 at 16:10 Adam Kemp via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org> > <mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>> wrote:
I know what the proposal said. I’m making a case that there
is value in
doing it differently.
The composability of futures is valuable. Mixing and matching
async/await with futures is also valuable. The
queue-returning behavior
that you can get from futures is also valuable, and building
async/await on top of futures means async/await can get that
for free.
Why couldn't you mix and match async/await and futures and get the
queue-return behavior of futures if futures are built on top of
async/await
instead off the other way around?
We could, but the syntax is much worse. Contrast:
*async/await built on top of Futures*
*
let image= preprocessImage(downloadImage())
let text= translate(downloadText())
awaitrender(image: image,text: text)
*Futures built on top of async/await*
*
let image= Future(downloadImage).then({preprocessImage($0) })
let text= Future(downloadText).then({translate($0) })
awaitrender(image: image.get(),text: text.get())
Maybe you don’t value those things, which is fine. But I do,
and maybe
other people do too. That’s why we’re having a discussion
about it.
It can also be valuable having a minimal implementation, but
we have to
acknowledge that it comes with a downside as well. The
problem with
doing a minimal implementation is that you can be stuck with the
consequences for a long time. I want to make sure that we’re
not stuck
with the consequences of a minimal implementation that doesn’t
adequately address the problems that async/await should be
addressing.
I’d hate for Swift to get an async/await that is so weak
that it has to
be augmented by tedious boilerplate code before it’s useful.
On Aug 28, 2017, at 1:54 PM, Wallacy <wallacyf@gmail.com > <mailto:wallacyf@gmail.com> > <mailto:wallacyf@gmail.com <mailto:wallacyf@gmail.com>>> > wrote:
We don't need to this now!
Again: (Using proposal words)
"It is important to understand that this is proposing
compiler support
that is completely concurrency runtime-agnostic. This
proposal does
not include a new runtime model (like "actors") - it
works just as
well with GCD as with pthreads or another API.
Furthermore, unlike
designs in other languages, it is independent of
specific coordination
mechanisms, such as futures or channels, allowing these
to be built as
library feature"
and
"This proposal does not formally propose a |Future|
type, or any other
coordination abstractions. There are many rational
designs for
futures, and a lot of experience working with them. On
the other hand,
there are also completely different coordination
primitives that can
be used with this coroutine design, and incorporating
them into this
proposal only makes it larger."
and
We focus on task-based concurrency abstractions commonly
encountered
in client and server applications, particularly those
that are highly
event driven (e.g. responding to UI events or requests
from clients).
This does not attempt to be a comprehensive survey of
all possible
options, nor does it attempt to solve all possible
problems in the
space of concurrency. Instead, it outlines a single
coherent design
thread that can be built over the span of years to
incrementally drive
Swift to further greatness.
and
This proposal has been kept intentionally minimal, but
there are many
possible ways to expand this in the future.
....
The point is: No Future type is indeed proposed yet!
The proposal try to include de "minimum" required to
implement a basic
async/await to solve the problem created by the GCD!
(Pyramid of doom)
The question is: How do you do the same using
dispatch_async ?
dispatch_async also does not return nothing to do what
you are
intentend do do!
Algo, by Swift 5 manifesto, there's no compromise to
make a "complete"
concurrency model by this time!
My intention is only make parity to dispatch_async, but
also make the
ground free to make more complex implementation like
Futures in
another round on top of this one.
This 'async T' can be a real type in the future? Maybe
will... But
doesn't matter now! Now we only need to is some kind of
type which
need to be unwrapped using await before use. Maybe this
intermediary/virtual type can be a real thing and gain
some abilities
at some point! Maybe a full Future type, why not?
Em seg, 28 de ago de 2017 às 17:33, Adam Kemp > <adam.kemp@apple.com <mailto:adam.kemp@apple.com> > <mailto:adam.kemp@apple.com > <mailto:adam.kemp@apple.com>>> escreveu:
How would these anonymous types get composed? If I
wanted to
implement a function that takes a collection of
futures and wait
on it, how would I do that? That is, how would I
implement the
equivalent of C#’s Task.WhenAll and Task.WhenAny
methods?
More generally, how do you pass one of these
typeless futures to
some other function so that we can do the waiting there?
On Aug 28, 2017, at 1:23 PM, Wallacy > <wallacyf@gmail.com <mailto:wallacyf@gmail.com> > <mailto:wallacyf@gmail.com > <mailto:wallacyf@gmail.com>>> wrote:
And that's why I (and others) are suggesting:
func processImageData1a() async -> Image {
let dataResource = async
loadWebResource("dataprofile.txt") //
No future type here... Just another way to call
dispatch_async
under the hood.
let imageResource = async
loadWebResource("imagedata.dat")
// ... other stuff can go here to cover load
latency...
let imageTmp = await decodeImage(dataResource,
imageResource) // Compiles force await call here...
let imageResult = await
dewarpAndCleanupImage(imageTmp)
return imageResult
}
And now we gain all advantages of async/await
again without to
handle with one more type.
Em seg, 28 de ago de 2017 às 17:07, Adam Kemp
via swift-evolution
<swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>>
escreveu:
I think the biggest tradeoff is clearer when
you look at the
examples from the proposal where futures are
built on top of
async/await:
func processImageData1a() async -> Image {
let dataResource = Future { await
loadWebResource("dataprofile.txt") }
let imageResource = Future { await
loadWebResource("imagedata.dat") }
// ... other stuff can go here to
cover load latency...
let imageTmp = await
decodeImage(dataResource.get(),
imageResource.get())
let imageResult = await
dewarpAndCleanupImage(imageTmp)
return imageResult
}
With this approach you have to wrap each
call site to create
a future. Compare to this:
func processImageData1a() -> Future<Image> {
let dataResourceFuture =
loadWebResource("dataprofile.txt”);
let imageResourceFuture =
loadWebResource("imagedata.dat”);
// ... other stuff can go here to
cover load latency...
let imageTmp = await decodeImage(await
dataResourceFuture, await
imageResourceFuture)
let imageResult = await
dewarpAndCleanupImage(imageTmp)
return imageResult
}
Here, not only are the explicit wrappers
gone, but this
function itself can be used with either
await or as a future.
You get both options with one implementation.
As I’ve mentioned before, C#’s
implementation is not tied to
any one particular futures implementation.
The Task type is
commonly used, but async/await does not
directly depend on
Task. Instead it works with any return type
that meets
certain requirements (detailed here:
https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/
<https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/>).
Swift could do this using a protocol, which
can be
retroactively applied using an extension.
Obviously for this to be useful we would
need some kind of
existing future implementation, but at least
we wouldn’t be
tied to any particular one. That would mean
library
maintainers who have already been using
their own futures
implementations could quickly adopt
async/await in their code
without having to rewrite their futures
library or throw
wrappers around every usage of async/await.
They could just
adopt a protocol (using an extension, even)
and get
async/await support for free.
The downside is that this feature would be
specific to the
async/await use case rather than a generic
coroutine
implementation (i.e., there would have to be
a separate
compiler transform for yield return). It’s
not clear to me
why it should be a goal to have just one
generic coroutine
feature. The real-world usages of
async/await and yield
return are different enough that I’m not
convinced we could
have a single compiler feature that meets
the needs of both
cleanly.
On Aug 27, 2017, at 7:35 PM, Florent Vilmart > <florent@flovilmart.com > <mailto:florent@flovilmart.com> > <mailto:florent@flovilmart.com > <mailto:florent@flovilmart.com>>> wrote:
Adam, you’re completely right, languages
as c# and JS have
been through the path before, (callback,
Promises ,
async/await) I believe Chris’s goal it
to avoid building a
promise implementation and go straight
to a coroutines
model, which is more deeply integrated
with the compiler. I
don’t see a particular trade off,
pursuing that route, and
the main benefit is that coroutines can
power any
asynchronous metaphor (Signals, Streams,
Futures, Promises
etc...) which is not true of Futures so
i would tend to
think that for the long run, and to
maximize usability,
async/await/yield would probably be the
way to go.
On Aug 27, 2017, 22:22 -0400, Adam Kemp > <adam.kemp@apple.com <mailto:adam.kemp@apple.com> > <mailto:adam.kemp@apple.com > <mailto:adam.kemp@apple.com>>>, wrote:
As has been explained, futures can
be built on top of
async/await (or the other way
around). You can have the
best of both worlds. We are not
losing anything by having
this feature. It would be a huge
improvement to have this
as an option.
However, using futures correctly
requires more nested
closures than you have shown in your
examples to avoid
blocking any threads. That's why
you're not seeing the
advantage to async/await. You're
comparing examples that
have very different behaviors.
That said, I have also expressed my
opinion that it is
better to build async/await on top
of futures rather than
the other way around. I believe it
is more powerful and
cleaner to make async/await work
with any arbitrary future
type (via a protocol). The
alternative (building futures on
top of async/await) requires more
code when the two are
mixed. I very much prefer how it's
done in C#, where you
can freely mix the two models
without having to resort to
ad-hoc wrappers, and you can use
async/await with any
futures implementation you might
already be using.
I really think we should be having
more discussion about
the tradeoffs between those two
approaches, and I'm
concerned that some of the opinions
about how C# does it
are not based on a clear and
accurate understanding of how
it actually works in that language.
-- Adam Kemp
On Aug 27, 2017, at 6:02 PM, Howard
Lovatt
<howard.lovatt@gmail.com
<mailto:howard.lovatt@gmail.com>
<mailto:howard.lovatt@gmail.com
<mailto:howard.lovatt@gmail.com>>>
wrote:
The async/await is very similar
to the proposed Future (as
I posed earlier) with regard to
completion-handler code,
they both re-write the imported
completion-handler
function using a closure, the
relevant sentence from the
Async Proposal is:
"Under the hood, the
compiler rewrites this code using
nested closures ..."
Unlike the proposed future code
the async code is not
naturally parallel, in the
running example the following
lines from the async code are
run in series, i.e. await
blocks:
let dataResource= awaitloadWebResource("dataprofile.txt")
let imageResource= awaitloadWebResource("imagedata.dat")
The equivalent lines using the
proposed Future:
let dataResource= loadWebResource("dataprofile.txt")
let imageResource= loadWebResource("imagedata.dat")
Run in parallel and therefore
are potentially faster
assuming that resources, like
cores and IO, are available.
Therefore you would be better
using a Future than an
async, so why provide an async
unless you can make a
convincing argument that it
allows you to write a better
future?
-- Howard.
On 28 August 2017 at 09:59, Adam > Kemp <adam.kemp@apple.com <mailto:adam.kemp@apple.com> > <mailto:adam.kemp@apple.com > <mailto:adam.kemp@apple.com>>> wrote:
This example still has
nested closures (to create a
Future), and still relies on
a synchronous get method
that will block a thread.
Async/await does not require
blocking any threads.
I’m definitely a fan of
futures, but this example
isn’t even a good example of
using futures. If you’re
using a synchronous get
method then you’re not using
futures properly. They’re
supposed to make it easy to
avoid writing blocking code.
This example just does
the blocking call on some
other thread.
Doing it properly would show
the benefits of
async/await because it would
require more nesting and
more complex error handling.
By simplifying the code
you’ve made a comparison
between proper asynchronous
code (with async/await) and
improper asynchronous code
(your example).
That tendency to want to
just block a thread to make
it easier is exactly why
async/await is so valuable.
You get simple code while
still doing it correctly.
-- Adam Kemp
On Aug 27, 2017, at 4:00 PM,
Howard Lovatt via
swift-evolution
<swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>> wrote:
The running example used
in the white paper coded
using a Future is:
func processImageData1()
-> Future<Image> {
return
AsynchronousFuture { _ -> Image in
let dataResource =
loadWebResource("dataprofile.txt") // dataResource
and imageResource run in
parallel.
let imageResource =
loadWebResource("imagedata.dat")
let imageTmp =
decodeImage(dataResource.get ?? Resource(path:
"Default data resource
or prompt user"),
imageResource.get ??
Resource(path: "Default image
resource or prompt user"))
let imageResult =
dewarpAndCleanupImage(imageTmp.get ??
Image(dataPath: "Default
image or prompt user",
imagePath: "Default
image or prompt user"))
return
imageResult.get ?? Image(dataPath:
"Default image or prompt
user", imagePath: "Default
image or prompt user")
}
}
This also avoids the
pyramid of doom; the pyramid is
avoided by converting
continuation-handlers into
either a sync or future,
i.e. it is the importer that
eliminates the nesting
by translating the code
automatically.
This example using
Future also demonstrates three
advantages of Future:
they are naturally parallel
(dataResource and
imageResource lines run in
parallel), they timeout
automatically (get returns
nil if the Future has
taken too long), and if there
is a failure (for any
reason including timeout) it
provides a method of
either detecting the failure or
providing a default (get
returns nil on failure).
There are a three of
other advantages a Future has
that this example
doesn’t show: control over which
thread the Future runs
on, Futures can be cancelled,
and debugging
information is available.
You could imagine
`async` as a syntax sugar for
Future, e.g. the above
Future example could be:
func processImageData1()
async -> Image {
let dataResource =
loadWebResource("dataprofile.txt") // dataResource
and imageResource run in
parallel.
let imageResource =
loadWebResource("imagedata.dat")
let imageTmp =
decodeImage(dataResource.get
?? Resource(path:
"Default data resource or prompt
user"),
imageResource.get ?? Resource(path: "Default
image resource or prompt
user"))
let imageResult =
dewarpAndCleanupImage(imageTmp.get ??
Image(dataPath: "Default
image or prompt user",
imagePath: "Default
image or prompt user"))
return
imageResult.get ?? Image(dataPath:
"Default image or prompt
user", imagePath: "Default
image or prompt user")
}
Since an async is sugar
for Future the async runs as
soon as it is created
(as soon as the underlying
Future is created) and
get returns an optional (also
cancel and status would
be still be present). Then if
you want control over
threads and timeout they could
be arguments to async:
func processImageData1()
async(queue:
DispatchQueue.main,
timeout: .seconds(5)) -> Image {
... }
On Sat, 26 Aug 2017 at
11:00 pm, Florent Vilmart
<florent@flovilmart.com
<mailto:florent@flovilmart.com>
<mailto:florent@flovilmart.com
<mailto:florent@flovilmart.com>>> wrote:
Howard, with async /
await, the code is flat and
you don’t have to
unowned/weak self to prevent
hideous cycles in
the callbacks.
Futures can’t do that
On Aug 26, 2017,
04:37 -0400, Goffredo Marocchi
via swift-evolution
<swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>>, wrote:
With both he now
built in promises in Node8 as
well as
libraries like Bluebird there was ample
time to evaluate
them and convert/auto convert
at times
libraries that loved callback pyramids
of doom when the
flow grows complex into promise
based chains.
Converting to Promises seems
magical for the
simple case, but can quickly
descend in hard
to follow flows and hard to
debug errors
when you move to non trivial multi
path scenarios.
JS is now solving it with their
implementation
of async/await, but the point is
that without the
full picture any single
solution would
break horribly in real life
scenarios.
Sent from my iPhone
On 26 Aug 2017,
at 06:27, Howard Lovatt via
swift-evolution
<swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>> wrote:
My argument
goes like this:
1. You
don't need async/await to write a
powerful
future type; you can use the
underlying
threads just as well, i.e. future
with
async/await is no better than future without.
2. Since
future is more powerful, thread
control,
cancel, and timeout, people should be
encouraged
to use this; instead because
async/await
are language features they will be
presumed,
incorrectly, to be the best way,
consequently
people will get into trouble with
deadlocks
because they don't have control.
3.
async/await will require some engineering
work and
will at best make a mild syntax
improvement
and at worst lead to deadlocks,
therefore
they just don't carry their weight in
terms of
useful additions to Swift.
Therefore,
save some engineering effort and
just provide
a future library.
To turn the
question round another way, in two
forms:
1. What
can async/wait do that a future can't?
2. How
will future be improved if async/await
is added?
-- Howard.
On 26 August
2017 at 02:23, Joe Groff
<jgroff@apple.com
<mailto:jgroff@apple.com>
<mailto:jgroff@apple.com
<mailto:jgroff@apple.com>>> wrote:
On
Aug 25, 2017, at 12:34 AM, Howard
Lovatt <howard.lovatt@gmail.com
<mailto:howard.lovatt@gmail.com>
<mailto:howard.lovatt@gmail.com
<mailto:howard.lovatt@gmail.com>>> wrote:
In
particular a future that is cancellable
is
more powerful that the proposed
async/await.
It's not
more powerful; the features are to
some
degree disjoint. You can build a
Future
abstraction and then use async/await
to sugar
code that threads computation
through
futures. Getting back to Jakob's
example,
someone (maybe the Clang importer,
maybe
Apple's framework developers in an
overlay)
will still need to build
infrastructure on top of IBActions and
other
currently ad-hoc signalling
mechanisms to integrate them into a more
expressive coordination framework.
-Joe
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
--
-- Howard.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org
<mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
<mailto:swift-evolution@swift.org <mailto:swift-evolution@swift.org>>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution
<https://lists.swift.org/mailman/listinfo/swift-evolution>