[swift-evolution] Proposal: Pattern Matching Partial Function (#111)

Dany St-Amant dsa.mls at icloud.com
Sun Feb 7 09:47:43 CST 2016

> Le 4 févr. 2016 à 02:41, Paul Ossenbruggen via swift-evolution <swift-evolution at swift.org> a écrit :
> What we are proposing is not really control flow, but a way of mapping values without side effects concisely. They are partial functions as Craig pointed out, that map every value of input to another value of output. That is, they must have be completely specified and return the same type. Switch statements may not check every output is returning a value which can lead to errors. Because there is one returned type, we are able to use type inference to determine the result type. 
> With this construct, it is harder for another programmer to go in and add another statement with side effects into the expression.
> The proposal is powerful in that it provides a very nice way of apply this same concept to values in containers not just individual values, no for-loop is necessary. 
> If the proposal is accepted, there will be essentially two closure forms,. the one we are all used to and a new one which uses case and default which is a partial function. 

Some might say that since functions can be use in lieu of closures (but not as a trailing one), that would be three types of function with different support for implicit return.

From what I remember, the current proposal comes from a wish to have the equivalent of ?: but with multiple values, which after multiple attempt evolved into:

{ cases 1:  “A”, 2: “B”, 3: “C”, default: “D” }

assuming the possibility of implicit returns. So let’s focus on implicit returns. Implicit returns in such short set of statements is elegant and it is hard for someone new to the code area to break it unintentionally. But, within longer sets of statements, it could be quite easy for one to unwillingly break the code. I have seen some code in bash where someone added logging data for a rare failure; on success the code kept working as intended, but on the rare failure, the code now started to return success due to the implicit return of the logging which was always successful. So I’m bit scared of expanding the current limited support for implicit return; will the compiler guarantee an error if one add code where he should not within the implicit returns. Once bitten, twice shy… More on such guarantee later

The current closure allows implicit returns only for single statement closure, you can have:

{ $0 > 10 }
{ $0 > 10 ? true : false }

but even though the resulting code is mainly the same, one cannot do:

{ if $0 > 10 { true } else  { false } } // doesn’t work

which I admit is not that readable, but is in a way is similar to the current switch proposal (not about readability, but about possible use), especially once you start nesting the if with condition ill suited for a switch statement. So if we put aside the not having to write the switch keyword, all the effort to support safe implicit returns for this proposal could be applicable to nested if. Of course at the cost of changing the propose syntax to:

{ switch $0 { case … } }

One example in the proposal contains two statements per conditional path, a let and an implicit return, such thing is currently prohibited in the current supported implicit returns (not sure why)

{ let x=$0; x > 10 } // doesn’t work

Declaring variable is benign so it could be allowed but to simplify lets assume we cannot.

IMHO, with the following rules for implicit returns:
- only supported for single statement (value, function) or exhaustive conditional (if, switch) which obey to this rule
- all implicit returns must have same type
most case should be covered, while protecting the code.

Good examples (according to the rule, but not so as real use cases)
{ $0 }
{ sqrt($0) }
{ if $0 != "data" { "generic" } else { switch $1 { case "r": "red"; default: "off" }}}

Compile errors:
{ print($0); $0 } // multiple statement
{ if $0 = 10 { "ten" } } // non-exhaustive
{ switch $0 { case "r": 10; default: "ten" } } // different type for implicit

As mentioned a few times, the parameters for such closure can be currently provided after the closure.

{ $0 > 10 ? "green" : "red" }(10)

But for long closure the parameters seem lost in a far away location, which have been a big deal in the thread. Assuming this implicit return part of this proposal get generalized, could we instead of the dedicated match function have a generic way to feed the parameters to the closure at the start, where it would make sense for the desired switch usage.

let str:String = (state) -> { switch $0 { case .Cold: "Too Cold"; case .Hot: "Too Hot"; default: "Just right" } }

which is not much longer than the current proposal

let str:String = match(state) { case .Cold: "Too Cold"; case .Hot: "Too Hot"; default: "Just right" }

Overloading -> could lead to confusion, but I could not come up with a clearer operator for "feed to".


> In one example in this thread a while ago, I took every example from the switch section of the swift book and they were all more simply and concisely done with expressions than statements. I also think this form might be used more than the switch statement once people get comfortable with it and as functional programming becomes more popular. 
> With the compact form, for simple mappings, you are able to be even more concise because the word “case” does not need to be repeated. 
> - Paul 

More information about the swift-evolution mailing list