[Concurrency] Fixing race conditions in async/await example

@Vladimir,

Default values are a problem for await/async when combined with parallel
running because await returns a value and not an optional (unlike future's
get). Below is a more realistic code for parallel running and a default
value using async/await:

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> 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>> 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>> wrote:

        On 29 Aug 2017, at 02:22, Xiaodi Wu via swift-evolution >>> <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>> >>> 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>> 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>> 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>> 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>>

                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.c
om/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>> 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>>, 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>> >>>>>>> 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>> 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>> 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>> 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>>,
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>>
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>> wrote:

                                On Aug 25, 2017, at 12:34 AM, >>>>>>>>>>>> Howard >>>>>>>>>>>> Lovatt <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>
                            https://lists.swift.org/mailma
n/listinfo/swift-evolution

                        --
                        -- Howard.
                        ______________________________
_________________
                        swift-evolution mailing list
                        swift-evolution@swift.org
                        <mailto:swift-evolution@swift.org>
                        https://lists.swift.org/mailma
n/listinfo/swift-evolution

                    _______________________________________________
                    swift-evolution mailing list
                    swift-evolution@swift.org <mailto:
swift-evolution@swift.org>
                    https://lists.swift.org/mailma
n/listinfo/swift-evolution

            _______________________________________________
            swift-evolution mailing list
            swift-evolution@swift.org <mailto:swift-evolution@swift.org>
            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

        _______________________________________________
        swift-evolution mailing list
        swift-evolution@swift.org <mailto:swift-evolution@swift.org>
        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

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution