[swift-evolution] What would public/open mean for a subtype? (was: Enums and Source Compatibility)

2017-08-11 Thread Daryle Walker via swift-evolution
> On Aug 9, 2017, at 3:57 PM, Adrian Zubarev via swift-evolution 
>  wrote:
> 
> I’d very much in favour of a consistent access modifiers across the whole 
> language and eliminate exclusive `open`. `open/public` protocols are more 
> than welcome. Plus it’s already has been said that Swift will potentially 
> support subtyping for value type in some future, where we’ll yet again would 
> need to align what `public` and `open` will mean. So I’d appreciate all the 
> steps that could already be made now to align their meaning as much as it’s 
> possible to this moment.

I have a proposal for “alternative types,” which can fulfill the “strong 
typedef” desired feature from C++. Since I changed from a custom initialization 
interface to RawRepresentable, I can do conditional translation to the new 
type, i.e. implement subtypes. (It can also do quotient types.) What do you 
mean by needing to figure out what public/open mean?

— 
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com 

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


Re: [swift-evolution] Enums and Source Compatibility

2017-08-11 Thread Jordan Rose via swift-evolution


> On Aug 11, 2017, at 02:59, Vladimir.S  wrote:
> 
> On 11.08.2017 2:37, Jordan Rose wrote:
>> Both you and Vladimir are bringing up this point, with Vladimir explicitly 
>> suggesting a "future" case that's different from "default". Again, the 
>> pushback we get here is that the "future" case is untestable…but maybe 
>> that's still an option worth having. 
> 
> I wonder, how the 'default' in exhaustive switch on open enum is testable?
> 
> I mean, let's say we have such enum in one of the frameworks:
> 
> open enum MyOpenEnum {
>  case one
>  case two
> }
> 
> , then in our code:
> 
> switch myOpenEnumInstance {
>  case .one : ...
>  case .two : ...
>  default : ... // how this can be tested?
> }
> 
> I just strongly feel that be able to keep switch exhaustive at the moment of 
> compilation - is critical requirement in some cases, when it is very 
> important to not forget to process some cases. With just 'default' in switch 
> for open enum - we are loosing this compiler's help. This is why 'future' 
> case is required for open enums.
> 
> Also, if I understand correctly, we are going to have most of all 
> extern(imported) enums as 'open'. So, we are loosing the feature to receive a 
> help for exhaustive switch from compiler for most of such enums.

That's true, but I don't know what to do about it. My hypothesis, again, is 
that this won't be common in practice; so far Charlie's been the only one to 
provide a real-world example of when this is useful.


> Moreover, shouldn't we just say that enums, that we have no Swift sources for 
> at the moment of compilation - should *always* be treated as 'open'? If we 
> compile our 'switch' together with the source of switched enum - such enum 
> can not be changed in the future.
> But, if enum is coming from other framework - we have no any control over it, 
> and even author of framework IMO can't be sure in most cases that enum will 
> not be extended in future, and probably we even should not ask author of 
> framework to consider its enum closed, as most likely one can't foresee for 
> sure.
> 
> Wouldn't this be a simpler and more robust model to think about enums in 
> Swift?
> So you know, that *any* enum coming from framework(not from source) - can be 
> changed in future, and so you have to process this case explicitly. In this 
> case we don't need to mark enum as 'open' or 'closed' at all, but for some 
> rare cases, when author of framework *really sure* enum can not be changed in 
> future(and future change in enum will break all the code depending on it), we 
> can introduce some 'final' marker(or @exhaustive directive) to help 
> compiler's optimizations.

Again, some enums really do want to be exhaustive: Foundation.ComparisonResult, 
Swift.Optional, and in general anything the framework owner really does want 
people to exhaustively switch over. These aren't just optimization concerns 
because they affect how people are expected to use the type. I think this all 
just means that you're on the side that "open" should be the default.


> 
> Btw, is open enum is allowed to change the type of associated value for some 
> cases or even enum's raw type? I.e. what changes in open enum will lead to 
> crash in our code and which will just be processed in 'default'/'future' 
> block in switch?

Nope, that's not planned to be allowed. That would break source compatibility 
outside of just switch—it would also affect `if case` as well as the creation 
of an enum with that case.

The last time I thought about this, I came up with this list of things we want 
to allow in "open" enums:

• Adding a new case.
• Reordering existing cases is a "binary-compatible source-breaking 
change". In particular, if an enum is RawRepresentable, changing the raw 
representations of cases may break existing clients who use them for 
serialization.
• Adding a raw type to an enum that does not have one.
• Removing a non-public, non-versioned case (if we ever have such a 
thing).
• Adding any other members.
• Removing any non-public, non-versioned members.
• Adding a new protocol conformance (with proper annotations).
• Removing conformances to non-public protocols.

We're now questioning whether reordering should be allowed at all for 
implementation reasons, but other than that this list should still be accurate.

> 
> Vladimir. (P.S. Sorry for long reply)

Thanks for thinking about this in detail; better to get the problems out in the 
open now before I write up a formal proposal!

Jordan

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


[swift-evolution] Async Affordances Wish List

2017-08-11 Thread Marc Schlichte via swift-evolution
Hi,

to help with async programming tasks, I could see the following affordances on 
async/await to be helpful:

1. Make existing Continuation Passing Style (CPS) APIs available via 
async/await.

2. Await with timeouts.

3. Possibility to cancel awaits.


To be more specific:


Ad 1. 

I think it could be helpful if an API like: 

`func foo(_ arg: Arg, completion: (val: Val) -> Void)`

could be invoked like this:

`let val: Val = await foo(arg)`

(i.e, having a mapped signature like: `func foo(_ arg: Arg) async -> Val`)


Often, the completion takes an error argument:

`func bar(_ arg: Arg, completion: (val: Val?, err: Error?) -> Void)`

Calling it via async/await might look like this:
```
do {
  let val: Val = try await bar(arg)
} catch {
  // handle error
}
```

I could imagine that new type attributes are needed to make this happen:

@continuation: the closure which continues the async control flow

`func bar(_ arg: Arg, completion: @continuation (val: Val?, err: Error?) -> 
Void)`

This function would transform to something like this:

`func bar(_ arg: Arg) throws async -> Val`


Some developers already have adopted a Result style - it would be nice to 
support the same conversion here too:

`func bar2(_ arg: Arg, completion: @continuation (result: Result) -> 
Void)` ->
`func bar2(_ arg: Arg) throws async -> Val`

Maybe `Result` has to adopt a specific protocol to allow this.


Ad 2 - Await with Timeouts

It could be helpful to have the option to specify how long an await should wait 
before throwing a timeout error:

```
do {
  let val = try await(for: 2.718) bar(arg)
} catch AsyncError.timeout {
  // handle timeout
} catch {
  // handle other errors
}
```


Ad 3 - Cancelation

Sometimes, I maybe want to cancel an async operation.

```
class C {
  var task: CancelableTask?
  
  func startProcess() {
do {
  task = baz(arg)
  let val = try await task!
  …
} catch AsyncError.cancelation {
  // handle cancelation
} catch {
  // handle other errors
}
  }

  func cancelProcess() {
task?.cancel()
  }
}
```

To be cancelable, a function needs to support that:
```
func baz(_ arg: Arg) -> CancelableTask {
let task = CancelableTask()
let sess = URLSession.dataTask(with: arg.url) { data, resp, err in
  // No automatic CPS conversion here as dataTask does not return Void
  task.finish(with: data.val)
}
task.onCancellation {
  sess.cancel()
}
return task
} 
```

`baz` would have an equivalent signature of:

`func baz(_ arg: Arg) throws async cancelable-> Val`

Then, the non-cancellable `bar` function from above could also be written as:

`func bar(_ arg: Arg) -> Task` (aka. Promise, Future, …)

and the non-throwing `foo` function:

`func foo(_ arg: Arg) -> NonThrowingTask`


Of course, work on concurrency in Swift has a much bigger scope than this 
laundry list of
 on async/await capabilities.

Cheers
Marc



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


Re: [swift-evolution] Enums and Source Compatibility

2017-08-11 Thread Vladimir.S via swift-evolution

On 11.08.2017 2:37, Jordan Rose wrote:
Both you and Vladimir are bringing up this point, with Vladimir explicitly suggesting 
a "future" case that's different from "default". Again, the pushback we get here is 
that the "future" case is untestable…but maybe that's still an option worth having. 


I wonder, how the 'default' in exhaustive switch on open enum is testable?

I mean, let's say we have such enum in one of the frameworks:

open enum MyOpenEnum {
  case one
  case two
}

, then in our code:

switch myOpenEnumInstance {
  case .one : ...
  case .two : ...
  default : ... // how this can be tested?
}

I just strongly feel that be able to keep switch exhaustive at the moment of 
compilation - is critical requirement in some cases, when it is very important to not 
forget to process some cases. With just 'default' in switch for open enum - we are 
loosing this compiler's help. This is why 'future' case is required for open enums.


Also, if I understand correctly, we are going to have most of all extern(imported) 
enums as 'open'. So, we are loosing the feature to receive a help for exhaustive 
switch from compiler for most of such enums.


Moreover, shouldn't we just say that enums, that we have no Swift sources for at the 
moment of compilation - should *always* be treated as 'open'? If we compile our 
'switch' together with the source of switched enum - such enum can not be changed in 
the future.
But, if enum is coming from other framework - we have no any control over it, and 
even author of framework IMO can't be sure in most cases that enum will not be 
extended in future, and probably we even should not ask author of framework to 
consider its enum closed, as most likely one can't foresee for sure.


Wouldn't this be a simpler and more robust model to think about enums in Swift?
So you know, that *any* enum coming from framework(not from source) - can be changed 
in future, and so you have to process this case explicitly. In this case we don't 
need to mark enum as 'open' or 'closed' at all, but for some rare cases, when author 
of framework *really sure* enum can not be changed in future(and future change in 
enum will break all the code depending on it), we can introduce some 'final' 
marker(or @exhaustive directive) to help compiler's optimizations.


Btw, is open enum is allowed to change the type of associated value for some cases or 
even enum's raw type? I.e. what changes in open enum will lead to crash in our code 
and which will just be processed in 'default'/'future' block in switch?


Vladimir. (P.S. Sorry for long reply)

(At the very least, it's worth recording in any eventual proposal why we /don't/ have 
it, and it could be added later if it turns out that was wrong.)


Thank you both for pushing on it.

Jordan


On Aug 9, 2017, at 21:55, Charlie Monroe > wrote:


Hi Jordan,

let's say I'm writing my custom number formatter and I switch 
over NSNumberFormatterStyle (NumberFormatter.Style in Swift) - the question always 
is what to do with the default case - it's a value that I am not programmatically 
counting with. I would personally just put in fatalError with a description that it 
was passed an unhandled style. Listing all enums in Foundation, I can see using 
most of them this way.


I personally have most of my switches exhaustive, mainly for the sake of being 
warned/error'ed when a new case is introduced - I've just done a quick search 
through my projects and I use default: usually for switching over non-enums 
(strings, object matching, ints, ...).


Maybe I'm in the minority here... Seemed like a good practice to me - usually the 
enum doesn't have but a few items on the list and you usually don't handle just 1-2 
cases in your switch, which makes the default label save you 1-2 lines of code that 
can save you from unnnecessarily crashing during runtime...


On Aug 10, 2017, at 1:57 AM, Jordan Rose > wrote:


Hi, Charlie. This is fair—if you're switching over an open enum at all, presumably 
you have a reason for doing so and therefore might want to handle all known cases 
every time you update your SDK. However, I think in practice that's going to be 
rare—do you have examples of exhaustive switches on SDK enums that exist in your 
own app?


(There's an additional piece about how to handle cases with different 
availability—there's nowhere obvious to write the #available.)


I suspect marking SDK enums "closed" will be much easier than nullability, simply 
because there are so few of them. Here's some data to that effect: out of all  60 
or so NS_ENUMs in Foundation, only 6 of them are ones I would definitely mark 
"closed":


- NSComparisonResult
- NSKeyValueChange / NSKeyValueSetMutationKind
- NSRectEdge
- NSURLRelationship
- /maybe/ NSCalculationError

There are a few more, like NSURLHandleStatus, where I could see someone wanting to 
exhaustively 

Re: [swift-evolution] [Review] SE-0185 - Synthesizing Equatable and Hashable conformance

2017-08-11 Thread Haravikk via swift-evolution

> On 10 Aug 2017, at 23:29, Brent Royal-Gordon  wrote:
> 
> If the only difference were whether the default implementation was generated 
> by a macro or not, would you still think auto-derivation should be marked 
> with a keyword?

Yes.

With or without a macro the issue here is still that the synthesised behaviour 
is generated based upon assumptions about a concrete type, whereas current 
default implementations are only based upon the methods/properties that are 
defined by the protocol itself, i.e- if the protocol is well-defined, and you 
implement it accordingly, then all a default implementation should be doing is 
correctly using the protocol. The problem with the synthesised behaviour is 
that it must necessarily make assumptions about parts of the concrete type that 
are not defined by the protocol and may in fact have nothing to do with it at 
all (e.g- could be properties implementing an entirely different protocol).

Whether or not this is generated by a macro is not the issue. If we assume 
macros will eventually replace features like this one, then I would still want 
a clear standard on how to distinguish between opting in to synthesised 
behaviour such as this.


To be clear as well; I didn't specifically request a keyword. In fact I'm 
leaning more towards separate protocols personally; i.e- if you just want to 
adopt the requirements you would conform to Equatable, but if you want to use 
the synthesised behaviour you would instead conform to AutoEquatable (or some 
similar naming convention). The important distinction here being that simply 
conforming to Equatable will still give you all the usual errors for failed 
requirements, at which point you can either fulfil them, or switch to 
AutoEquatable instead if you prefer.

This kind of standard would make sense for any protocol that wants to add 
default implementations that are "invasive", i.e- delve deeper into the 
concrete type than simply what the protocol itself defines as possible, i.e- 
behaviours that by their very nature must make assumptions about the concrete 
type. This might be fine for a protocol that is clearly defined as having such 
behaviour from the start, but personally I'd rather see a standard set that has 
it split off into a more specific protocol, such that we still have "pure" 
protocols which remain primarily as a set of requirements.

In other words, Equatable would contain all the basic requirements and the "non 
invasive" default implementations, while AutoEquatable would extend it with the 
macros/special behaviour that provides the synthesised defaults, allowing a 
developer to opt-in to them explicitly.

> On 10 Aug 2017, at 16:19, Tony Allevato  wrote:
> 
> I disagree that @transient is crucial for this feature to work—that feature 
> only applies to the set of types that contain volatile data, that volatile 
> data and all the other fields are all Equatable/Hashable, and the user wants 
> to synthesize Eq/Hash for a subset of its fields. I certainly agree that such 
> a set is non-empty, but I it's also small enough that it can be considered at 
> a later date.

I disagree with your reasons for disagreeing 

The problem is that synthesising the behaviour for such types right now will 
introduce a bug, and one that will not be exposed to developers except at 
runtime, which is something that makes me very uncomfortable. To be clear, my 
point is that a transient feature is a requirement now specifically because the 
proposal seeks to use conforming to Equatable as sufficient as an opt-in; if a 
more explicit opt-in were required then this would not be an issue, i.e- if a 
developer conforms to AutoEquatable instead of Equatable, then we can assume 
that they should know what that means, and should not do it unless they know 
their type contains no transient values.

With that kind of absolutely explicit opt-in then, yes, a @transient attribute 
or whatever can be deferred to a future feature. But with opt-in via Equatable 
conformance the lack of such a feature is much more pronounced. Though like I 
say, my preference is very much towards a more explicit opt-in, rather than an 
attribute/keyword that developers may not realise they need to use with regular 
Equatable (remember, not everyone will know that Equatable is gaining this new 
behaviour).

> I sympathize with these concerns, and a great deal of thought and discussion 
> was done around the best way to surface this feature in the last couple of 
> mailing list threads about this topic. But at the time this proposal and its 
> implementation were written, the synthesis for Codable had already been 
> implemented and this proposal adopts the same synthesis conditions that were 
> used by Codable. Core team members expressed that the implementation for 
> Eq/Hash should follow those existing practices as closely as possible; I 
> agreed that consistency is important so that we don't have to