Two comments: 1. I don't think `Future` typed arguments and returns are a big deal, since they give you easy parallelism, timeouts, queue control, and cancel and you just call `get` when you need a value.
2. Don't use `Future` typed arguments and returns where you don't want parallelism etc. EG if say that `processingImage`, `translate`, and `render` can run serially in the current `Future` and only the downloads need to be in parallel then none of the three `func`s would accept or return a `Future`: let image = preprocessImage(downloadImage().get ?? defaultImage) let text = translate(downloadText().get ?? defaultText) render(image, text) -- Howard. On 30 August 2017 at 15:29, David Hart <da...@hartbit.com> wrote: > I understand. But it’s quite problematic to have to write all Future > returning functions with Future inputs just to be able to support parallel > computations. It’s not how futures are using in C# and JavaScript. > > > On 30 Aug 2017, at 03:02, Howard Lovatt <howard.lov...@gmail.com> wrote: > > @David, > > The signatures would be: > > > func processImage(_ image: Future<Image>) -> Future<Image> > > func translate(_ text: Future<String>) -> Future<Image> > > > Inside `processImage` and `translate` you would `get` the values at the > point were needed so that downloadImage and downloadText run in parallel > (which is highly desirable). > > > -- Howard. > > On 30 August 2017 at 07:21, David Hart <da...@hartbit.com> wrote: > >> I don’t think the examples are 100% equivalent. In your version with the >> Future library, *preprocessImage* and *translate* need to accept futures >> as argument, correct? That’s more restrictive than in my example code where >> async/await specifically provide sugar over *then*. Plus I don’t >> understand why you mention that the Future version handles errors when >> async/await also plays very nicely with errors. >> >> On 29 Aug 2017, at 10:22, Howard Lovatt <howard.lov...@gmail.com> wrote: >> >> @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 >> parallellet 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> wrote: >> >>> >>> On 29 Aug 2017, at 02:22, Xiaodi Wu via swift-evolution < >>> swift-evolution@swift.org> wrote: >>> >>> On Mon, Aug 28, 2017 at 16:10 Adam Kemp via swift-evolution < >>> 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()) >>> await render(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) }) >>> await render(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 <walla...@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.k...@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 <walla...@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> 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/). >>>>>> 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 <flor...@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.k...@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.lov...@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 = await loadWebResource("dataprofile.txt") >>>>>> let imageResource = await loadWebResource("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.k...@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> 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 < >>>>>>> flor...@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>, 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> 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 <jgr...@apple.com> wrote: >>>>>>>> >>>>>>>>> >>>>>>>>> On Aug 25, 2017, at 12:34 AM, Howard Lovatt < >>>>>>>>> howard.lov...@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 >>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>> >>>>>>>> -- >>>>>>> -- Howard. >>>>>>> >>>>>>> _______________________________________________ >>>>>>> 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 >>>>>> >>>>> >>>>> >>>> _______________________________________________ >>>> 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 >>> >>> >>> >>> _______________________________________________ >>> 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