[swift-evolution] [proposal draft] new syntax to access a given case's payload

Jérôme Duquennoy jerome+swift at duquennoy.fr
Tue Sep 27 06:59:57 CDT 2016


This pattern matching syntax is very nice, for sure.
I think the proposed evolution would be a nice complement to it in two ways:
- being able to handle the case of optional using case? would avoid having to declare a case (.null in the exemple) that is purely technical, and does not match what the enum describes ;
- being able to assign a constant or variable outside of a flow control structure like “if" or “guard" can be useful (if you need to use the variable in multiple distinct places for exemple, be it in the same method, in different methods, or even in an instance variable) ;

But your point emphasise the second drawback I listed : in all those existing syntaxes, the parenthesis after the enum case contains the variable in which the payload will be stored.
The syntax I am suggesting does not follow the same logic, which is not nice.

That syntax would be closer:

let case? .enumCase(payloadItem1, payloadItem2) = input

It is pretty similar to the syntax used for pattern matching in guard, for and if.
The let and case keywords are swapped, the case gains a ‘?’ to reflect the fact that it will produce an optional, but the rest is the usual pattern matching syntax.

This solution would be even closer:

case? let .enumCase(payloadItem1, payloadItem2) = input

It mostly boils down to removing the guard keyword and its associated else.
But I feel very weird not having the let or var at the beginning of the line. Is that only me being too used to that, or does that matter for you too ?

Jerome


> On 27 Sep 2016, at 01:04, Robert Widmann <devteam.codafi at gmail.com> wrote:
> 
> Being a power user of this feature, I don’t think the existing syntax is cumbersome enough to warrant this kind of shortcut.  I really like being able to
> 
> guard case let .dict(book) = data else {
> 	// bail out
> }
> guard case let .dict(author) = book["author"] ?? .null else {
> 	// bail out
> }
> guard case let .integer(age) = author["age"] ?? .null else {
> 	// bail out
> }
> 
> // now we have the age
> 
> or more succinctly
> 
> guard case let .dict(book) = data,
>       case let .dict(author) = book["author"] ?? .null,
>       case let .integer(age) = author["age"] ?? .null 
> else {
> 	// bail out
> }
> // now we have the age
> 
> Because it forces me to think about the bailout case(s) and is really not that much longer than your proposed syntax
> 
> guard let book = case? .dict(inputData), 
>       let author = case? .dict(book?["author”]), 
>       let age = case? .integer(author?["age”])
> else {
> 	// bail out
> }
> // now we have the age
> 
> ~Robert Widmann
> 
>> On Sep 26, 2016, at 11:51 AM, Jérôme Duquennoy via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> Summary
>> The aim of this proposal is to offer a new syntax to ease some uses of enums with payload.
>> 
>> Situation to improve:
>> Enums makes it possible to have explicate typing where it was not possible before. A classic example of that is filling a dictionary with data coming from a file or a stream (json, plist, …) : the types of possible values is finite : arrays, dicts, int, double, bool or string for json for exemple.
>> An enum can represent this finite range of possible types, its make the code and the API more self-documented.
>> Then, you have two possibilities to deal with this enum:
>> - using switch statements
>> - using the if case syntax introduced by swift 2
>> 
>> The drawback is that those two solutions can lead to writing code with high visual complexity, even though the logic behind is pretty simple.
>> 
>> Consider this example of a data dictionary, that a web service could have returned:
>> - book
>>   - title: 
>>   - author:
>>     - name: Apple
>>     - age: 40
>> 
>> We can decode this in a variable of type [String:Value], where Value is:
>> 
>> enum Value {
>>   case integer(value: Int)
>>   case string(value: String)
>>   case dict(value: [String:Value])
>>   case null
>> }
>> 
>> Here is a snippet of code to access the age of the author:
>> 
>> if case .dict(let book) = data {
>>   if case .dict(let author) = book["author"] ?? .null {
>>     if case .integer(let age) = author["age"] ?? .null {
>>       // now we have the age
>>     }
>>   }
>> }
>> 
>> The multiple indentation levels can rapidly make this code unattractive to read, and we need to add a null case to the enum to deal with optional values.
>> 
>> Proposed solution:
>> I suggest to add a new syntax, using the case keyword to ease access to the payload of such enums :
>> 
>> let payloadContent = case? .enumCase(variable)
>> 
>> The payloadContent variable will be an optional, that can be either nil, or contain the payload of enumCase.
>> If the payload contains multiple variables, payloadContent will be a tupple.
>> This syntax can accommodate an optional variable as an input. If the value of variable is nil, then payloadContent will be nil.
>> Only enum cases with a payload can be used with this syntax (it would make no sens for cases without a payload).
>> 
>> With that syntax, the null case of the enum can be removed, and the code to access the age becomes:
>> 
>> let book = case? .dict(inputData)
>> let author = case? .dict(book?["author"])
>> let age = case? .integer(author?["age"])
>> 
>> Advantages:
>> - It leverages the well established notion of optional, and similar logic already exists in the language (for the as? operator notably).
>> - It does not add a new keyword
>> - It promotes the use of enum to enforce explicit typing, which leads to more self-documenting code
>> - It reduces the complexity of the code in situations such as the one of the exemple
>> 
>> Drawbacks:
>> - It adds a third use of the case keyword. 
>> - In the proposed syntax, the variable between parenthesis is not the payload, but the variable to decode. This might be disturbing, as it differs from the other syntax of enum values.
>> - If the payload is an optional, it is not possible to differentiate a non-matching case and a matching case a nil payload.
>> 
>> Alternatives:
>> - Another syntax without parenthesis could be used to avoid the second drawback:
>> let payload = case? .enumCase variable
>> 
>> Impact on existing code:
>> None, this is adding a new syntax
>> 
>> 
>> This proposal would have no impact on the ABI, so it probably does not fit the stage 1 of swift 4’s roadmap. But I would be glad to have your feedback, so that I can have a proposal ready once we enter stage 2.
>> So what your thoughts on that proposal ?
>> 
>> Thanks
>> 
>> Jerome
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160927/e8790b63/attachment.html>


More information about the swift-evolution mailing list