Proposal: Pattern Matching Partial Function (#111)


(Craig Cruden) #1

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

Proposal: SE-0024 <https://github.com/apple/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md>
Author(s): Craig Cruden <https://github.com/cacruden>
Status: Awaiting review
Review manager: TBD
<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#introduction>Introduction

Provide the ability for defining partial functions using familiar case/default pattern matching syntax. A new global matchfunction would use this to provide switch-expression functionality for all types. This function would accept a value and a closure containing the case/default partial functions. Combining case/default partial functions in a closure must always be exhaustive providing a total function. Functions on arrays, dictionaries or other collections such as reduce orfilter, or rather all higher order functions taking a unary function as argument, would accept these closures as they are just normal closures.

Swift-evolution thread: ternary operator ?: suggestion <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006194.html>
<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#motivation>Motivation

There is currently no elegant methodology within Swift closures to conditionally map values. This type of scenario occurs often in Swift code and this proposal provides a generalized and flexible solution. Anywhere a closure is used the developer will also be able to conditionally execute multiple partial closures depending on the mapping of values. The syntax proposed makes Swift code easier to read, easier to write, and less issue prone.

The following example where a string is mapped to an enum case is indicative of the problem:

let str:String

switch state {
case .Cold:
    str = “Too cold”
case .Hot:
    str = “Too hot”
default:
    str = “Just right”
}
The syntax above does not elegantly express our intent as the switch statement is designed for “flow control”. Aside from being limited to choosing a single value our example requires no “flow control”. In addition, the ability to execute unrelated statements inside the switch statement makes it harder prove correctness of the algorithm.

Alternatives to using switch do exist but are unsatisfactory. A Dictionary, for example, increases cognitive load (i.e. requires Hashable keys and returns an optional). This makes it less clear as to what function is being performed.Dictionary also lacks exhautiveness or uniqueness checks which may result in unexpected runtime exceptions.

This proposal provides a simple mechanism which covers many different types of scenarios. It may be used where the ideal solution is a switch expression. It may also be used to provide a way of simply mapping conditionally between multiple values in two related but different domains. In addition, it can be used in combination with other functions such as reduceand filter where the logic is partially conditional.

The syntax is based on familiar case/default clauses common to switch statements which may be used in functions such as map, filter, reduce. This makes it easy for the developer to remember and understand.

<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#proposed-solution>Proposed solution

Any function which accepts a closure would also be able to accept a closure composed of partial functions defined usingcase/default syntax used in switch-case. Each case or defaultis considered a partial function since it defines the closure for a subset of values.

public enum Trade {
    case Buy(quantity: Double, price: Double)
    case Sell(quantity: Double, price: Double)
}

let commissions = trades.map {
    case .Buy(let quantity, let price) where quantity * price > 10000:
        let vipCommissionRate = calculateCommissionRate(...)
        quantity * price * vipCommissionRate / 100
    case .Buy(let quantity, let price):
        let commissionRate = calculateCommissionRate(...)
        quantity * price * commissionRate / 100
    case .Sell(let quantity, let price) where quantity * price > 10000:
        let vipCommissionRate = calculateCommissionRate(...)
        quantity * price * vipCommissionRate / 100
    case .Sell(let quantity, let price):
        let commissionRate = calculateCommissionRate(...)
        quantity * price * commissionRate / 100
}
Each of the case clauses represent a partial closure / partial function. As with a total closure the case result may be specified with the optional return clause. For example:

case .Buy(let quantity, let price) where quantity * price > 10000:
    let vipCommissionRate = calculateCommissionRate(...)
    return quantity * price * vipCommissionRate / 100
Alternative grammar cases/default which is a specialized version that can be used for the simplest and the most consise use case. If the use case is a simple definition of mapping of values then cases can be used to define multiple case clauses. cases clause may not be used with a where clause. The purpose of allowing cases as syntatic sugar for multiple case clauses is to allow a less verbose option for use cases where the developer would use a multi-case ternary expression.

let col = [1,5,7,9]

let newCol = col.map {
    cases 1: "one", 2: "two", 3: "three", 4: "four", 5: "five",
          6: "six", 7: "seven", 8: "eight", 9: "nine", 0: "zero"
    default: ""
}
The following global match function would provide switch-expression functionality to all types:

func match<T,U>(x: T, @noescape mapping: T -> U) -> U {
    return mapping(x)
}
The match function would be used similar to the following:

let n = 5

let s = match(n) {
    cases 1: "one", 2: "two", 3: "three", 4: "four", 5: "five",
          6: "six", 7: "seven", 8: "eight", 9: "nine", 0: "zero"
    default: ""
}
Functions such as reduce that receive two or more values must be enclosed in brackets (x, y) to otherwise the parser would likely have trouble distinquishing between comma delimited lists of values which are currently allowed as a single case.

public enum Troy {
    case Pound(Int)
    case Ounce(Int)
    case Pennyweight(Int)
    case Grain(Int)
}

let weightTroy = [Troy.Pound(5), Troy.Ounce(4), Troy.Pennyweight(6), Troy.Grain(9)]

let weightKg = weightTroy.reduce(0.00) {
    case (let acc, Troy.Pound(let quantity)):
        acc + Double(quantity) * 0.373
    case (let acc, Troy.Ounce(let quantity)):
        acc + Double(quantity) * 0.031103
    case (let acc, Troy.Pennyweight(let quantity)):
        acc + Double(quantity) * 0.001555
    case (let acc, Troy.Grain(let quantity)):
        acc + Double(quantity) * 0.0000648
}
<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#detailed-design>Detailed design

Any function which accepts a closure would also be able to accept a closure composed of partial functions defined usingcase/default syntax used in switch-case. Each case or defaultis considered a partial function since it defines the closure for a subset of values.

GRAMMAR OF A CLOSURE COMPOSED OF PARTIAL FUNCTIONS

closure → ­ {­switch-cases­opt­}­
switch-cases → switch-case­ switch-cases­opt
switch-case → case-label­ statements­ | default-label­ statements
case-label → case­ case-item-list­ :­
case-item-list → pattern­ where-clause­opt­ | pattern ­where-clause­opt­ , ­case-item-list­
default-label → default­ :­
where-clause → where­ where-expression­
where-expression → expression­

Alternative grammar cases/default which is a specialized version that can be used for the simplest and the most consise use case. If the use case is a simple definition of mapping of values then cases can be used to define multiple case clauses. cases clause may not be used with a where clause. The purpose of allowing cases as syntatic sugar for multiple case clauses is to allow a less verbose option for use cases where the developer would use a multi-case ternary expression.

NEW GRAMMAR FOR CONSISE FORM OF CASES

closure → ­ {­switch-cases­opt­}­
switch-cases → cases-label­ statements­ | default-label­ statements
cases-label → cases­ case-item-map­
case-item-map → pattern­ : value | pattern : value ­ , ­case-item-list­
default-label → default­:­

<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#impact-on-existing-code>Impact on existing code

This mechanism is opt-in, so existing code won't be affected by this change.

<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#alternatives-considered>Alternatives considered

There were various specialized multi-ternary suggestions made as a replacement/expansion of ternary which supported more than two possible examples, but are more of a specialized case that this proposal should eliminate the need for.

The only other alternative considered that was a generalized version similar to the proposal above was basically the same but using in instead of : because we were defining partial functions and not a switch command. The overwelming sentiment was in favour of using exactly the same syntax as the switch command where possible.

public enum Troy {
    case Pound(Int)
    case Ounce(Int)
    case Pennyweight(Int)
    case Grain(Int)
}

let weightTroy = [Troy.Pound(5), Troy.Ounce(4), Troy.Pennyweight(6), Troy.Grain(9)]

let weightKg = weightTroy.reduce(0.00) {
    case (let acc, Troy.Pound(let quantity)) in
        acc + Double(quantity) * 0.373
    case (let acc, Troy.Ounce(let quantity)) in
        acc + Double(quantity) * 0.031103
    case (let acc, Troy.Pennyweight(let quantity)) in
        acc + Double(quantity) * 0.001555
    case (let acc, Troy.Grain(let quantity)) in
        acc + Double(quantity) * 0.0000648
    }
}
<https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md#out-of-scope>Out of Scope

Named partial functions are out of scope for this proposal.


(Charles Constant) #2

I'm still +1 on this.

I would very much enjoy being able to do this:

let str = state.map {

  case .Cold: “Too cold”

  case .Hot: “Too hot”

  default: “Just right”

}


(Charles Constant) #3

While that’s slightly noisier, I’m not convinced the minor additional

concision justifies the substantial additional language complexity.

I'd definitely prefer it were even shorter (I actually made suggestions
earlier on this list, but they didn't gain much support).

But I'll take "little too verbose" over "much too verbose" any day of the
week.

Unfortunately, the problem with figuring out what is "too verbose" is that
it's so subjective.


(Charles Constant) #4

@Paul

On the more objective side of things, there is also the fact that, as the
proposal states, it's nice to have protection against this happening:

    let str: String
    switch(state) {
        case .Cold: str = "Too cold"; self.freezer.stop()
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }


(Paul Ossenbruggen) #5

Hi Paul,

Your example, can’t use type inference to determine the type of str which feels clumsy, and “str =“ is duplicated over and over. You also have no guarantee that str get assigned in each case. While this example that error is not likely, if your cases are more complex that could become a problem. Also, if I am writing something that is functional, it is less likely, as Charles points out, another developer will stick some side effect based code in your cases. Finally, for the proposal you could write it even more compactly: Switch statements seem much more heavyweight.

let str = match(state) { cases .Cold: “Too cold”, .Hot: “Too hot”
      default: “Just right” }

Cheers,
- Paul

···

On Jan 28, 2016, at 11:37 PM, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

@Paul

On the more objective side of things, there is also the fact that, as the proposal states, it's nice to have protection against this happening:

    let str: String
    switch(state) {
        case .Cold: str = "Too cold"; self.freezer.stop()
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

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


(Charles Constant) #6

Sorry for the barrage here, Paul :slight_smile:

I’m not even convinced the new syntax is clearer so much as cleverer.

As I've followed the various threads for switch-like assignments, I've been
thinking a lot about how valid it is to say that more explicit is all that
helpful to beginners. I wish I had the resources to test these assumptions
with actual people who had never written Swift.

As an example.

We assume that this:

    let phrase: String

    switch( temperature ) {

        case .Cold: str = "Too cold"

        case .Hot: str = "Too hot"

        default: str = "Just right"

    }

is going to be easier for beginners than something like this:

    let phrase = temperature ?

        "Too cold" if .Cold :

        "Too hot” if .Hot :

        "Just right" if _

I'm using a short version here that is not the actual proposal, and that we
won't use (since we don't want to overload "if"). But I have a strong hunch
that a beginner is not going to find the longer version any more clear.
There's a lot of syntax for the programmer to parse in the "switch"
statement, as it stands. I suspect the less redundancy we have, the easier
it is to figure out what is going on.


(Paul Cantrell) #7

The language currently allows this:

    let str: String
    switch(state) {
        case .Cold: str = "Too cold"
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

While that’s slightly noisier, I’m not convinced the minor additional concision justifies the substantial additional language complexity. I’m not even convinced the new syntax is clearer so much as cleverer.

I’d need to see a much more compelling example to be in favor of this proposal.

Cheers,

Paul

···

On Jan 29, 2016, at 1:01 AM, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

I'm still +1 on this.

I would very much enjoy being able to do this:

let str = state.map {
  case .Cold: “Too cold”
  case .Hot: “Too hot”
  default: “Just right”
}


(James Campbell) #8

I think all of this could be solved by just having postfix if statements

var str: String?

str = "hot" if state == .Hot
str = "cold" if state == .Cold

//if the ??= operator proposal is accepted then we could just do:
str ??= "Invalid Temperature"

This is used a lot in Ruby, more flexible since it could be used on any line of code, flexible and concise. I think it reads quite well so beginners will grasp it easily (as I did when writing my first Ruby)

I think compiler wise it could be treated like a do while loop except it only loops once :wink:

···

Sent from my iPhone

On 29 Jan 2016, at 08:06, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

Sorry for the barrage here, Paul :slight_smile:

> I’m not even convinced the new syntax is clearer so much as cleverer.

As I've followed the various threads for switch-like assignments, I've been thinking a lot about how valid it is to say that more explicit is all that helpful to beginners. I wish I had the resources to test these assumptions with actual people who had never written Swift.

As an example.

We assume that this:

    let phrase: String
    switch( temperature ) {
        case .Cold: str = "Too cold"
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

is going to be easier for beginners than something like this:

    let phrase = temperature ?
        "Too cold" if .Cold :
        "Too hot” if .Hot :
        "Just right" if _

I'm using a short version here that is not the actual proposal, and that we won't use (since we don't want to overload "if"). But I have a strong hunch that a beginner is not going to find the longer version any more clear. There's a lot of syntax for the programmer to parse in the "switch" statement, as it stands. I suspect the less redundancy we have, the easier it is to figure out what is going on.

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


(Craig Cruden) #9

So the solution to everything in the proposal is to create a mutable variable and then a bunch of if statements in inverse format?

Then take that and embed in a “pure” function where you need closures?

And forget about pattern matching and decomposing enums?

And this is the solution to “all”?

···

On 2016-01-29, at 16:09:19, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

I think all of this could be solved by just having postfix if statements

var str: String?

str = "hot" if state == .Hot
str = "cold" if state == .Cold

//if the ??= operator proposal is accepted then we could just do:
str ??= "Invalid Temperature"

This is used a lot in Ruby, more flexible since it could be used on any line of code, flexible and concise. I think it reads quite well so beginners will grasp it easily (as I did when writing my first Ruby)

I think compiler wise it could be treated like a do while loop except it only loops once :wink:

Sent from my iPhone

On 29 Jan 2016, at 08:06, Charles Constant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sorry for the barrage here, Paul :slight_smile:

> I’m not even convinced the new syntax is clearer so much as cleverer.

As I've followed the various threads for switch-like assignments, I've been thinking a lot about how valid it is to say that more explicit is all that helpful to beginners. I wish I had the resources to test these assumptions with actual people who had never written Swift.

As an example.

We assume that this:

    let phrase: String
    switch( temperature ) {
        case .Cold: str = "Too cold"
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

is going to be easier for beginners than something like this:

    let phrase = temperature ?
        "Too cold" if .Cold :
        "Too hot” if .Hot :
        "Just right" if _

I'm using a short version here that is not the actual proposal, and that we won't use (since we don't want to overload "if"). But I have a strong hunch that a beginner is not going to find the longer version any more clear. There's a lot of syntax for the programmer to parse in the "switch" statement, as it stands. I suspect the less redundancy we have, the easier it is to figure out what is going on.

_______________________________________________
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


(Radek Pietruszewski) #10

I certainly would prefer if we could use switch (and if/else for that matter) as an expression, instead of some magic syntax usable in `map`. so:

let str = switch(state) {
        case .Cold: "Too cold"
        case .Hot: "Too hot"
        default: "Just right"
    }

— Radek

···

On 29 Jan 2016, at 08:10, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 29, 2016, at 1:01 AM, Charles Constant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'm still +1 on this.

I would very much enjoy being able to do this:

let str = state.map {
  case .Cold: “Too cold”
  case .Hot: “Too hot”
  default: “Just right”
}

The language currently allows this:

    let str: String
    switch(state) {
        case .Cold: str = "Too cold"
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

While that’s slightly noisier, I’m not convinced the minor additional concision justifies the substantial additional language complexity. I’m not even convinced the new syntax is clearer so much as cleverer.

I’d need to see a much more compelling example to be in favor of this proposal.

Cheers,

Paul

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


(Denis Nikitenko) #11

+1 from me - I would like to see this discussion continue. Swift already recognizes the utility of pattern matching for initialization by retaining the ?: operator. It would be nice to see a more general and flexible solution that is still reasonably concise and doesn’t introduce new keywords and operators - though I’m sure at least some changes to the language would be inevitable.

···

On Jan 29, 2016, at 1:43 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org> wrote:

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

<snip>


(Thorsten Seitz) #12

Hi Craig,

the following sentence is a bit misleading as it makes the feature seem a special ability on functions which accept closures which I do not think it should be.

···

Am 29.01.2016 um 07:43 schrieb Craig Cruden via swift-evolution <swift-evolution@swift.org>:

Any function which accepts a closure would also be able to accept a closure composed of partial functions defined usingcase/default syntax used in switch-case.

I think it should be a new feature how unary closures can be written, so I would reword it a little bit:

"Any closure that takes one argument can be composed of ..."

This would make your example of using it with reduce() invalid, though, but I think this would create ambiguities with cases where the closure is actually called with a tuple as single argument anyway (similar to the problems of the current autosplatting of tuples as function arguments which Chris want's to remove because of the subtle problems this introduces). It probably would be sufficient to just use match() within the reduction closure.

I still don't like the shortened "cases" variant and would prefer to drop it or at least replace or extend the match() example with a standard variant making it look like a "real" case expression.

-Thorsten


(Charles Constant) #13

I just used that example with "if" to try to make a point that it's not
always self-evident whether brevity is less clear. Maybe that was a poor
choice, because I was attempting to justify my support of Craig's proposal.
I don't agree that proposal is a case of "cleverer but not clearer." On the
contrary, it's probably easier for a beginner to parse, than a "switch" as
it stands.

I think all of this could be solved by just having postfix "if" statements

I don't think the Swift team would go for that, since they've worked hard
to avoid overloading statements in the rest of the language. In previous
iterations of this discussion thread, we've already discussed variations
like "when" and "on" etc.


(Craig Cruden) #14

The case partial functions are available in all closures, plus you still have the option to write your syntax based on the proposal with the only difference being the keyword of match vs switch.

let str = match(state) {
  case .Cold: “Too cold”
  case .Hot: “Too hot”
  default: “now it is just boring”
}

match just being a function that takes a value and a closure.

···

On 2016-01-31, at 2:11:20, Radosław Pietruszewski via swift-evolution <swift-evolution@swift.org> wrote:

I certainly would prefer if we could use switch (and if/else for that matter) as an expression, instead of some magic syntax usable in `map`. so:

let str = switch(state) {
        case .Cold: "Too cold"
        case .Hot: "Too hot"
        default: "Just right"
    }

— Radek

On 29 Jan 2016, at 08:10, Paul Cantrell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 29, 2016, at 1:01 AM, Charles Constant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'm still +1 on this.

I would very much enjoy being able to do this:

let str = state.map {
  case .Cold: “Too cold”
  case .Hot: “Too hot”
  default: “Just right”
}

The language currently allows this:

    let str: String
    switch(state) {
        case .Cold: str = "Too cold"
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

While that’s slightly noisier, I’m not convinced the minor additional concision justifies the substantial additional language complexity. I’m not even convinced the new syntax is clearer so much as cleverer.

I’d need to see a much more compelling example to be in favor of this proposal.

Cheers,

Paul

_______________________________________________
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


(Paul Ossenbruggen) #15

Agreed. I really would like this to move forward.

···

On Feb 2, 2016, at 6:59 PM, Denis Nikitenko via swift-evolution <swift-evolution@swift.org> wrote:

+1 from me - I would like to see this discussion continue. Swift already recognizes the utility of pattern matching for initialization by retaining the ?: operator. It would be nice to see a more general and flexible solution that is still reasonably concise and doesn’t introduce new keywords and operators - though I’m sure at least some changes to the language would be inevitable.

On Jan 29, 2016, at 1:43 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

<snip>

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


(Howard Lovatt) #16

If values and objects were made Collections of exactly one value then you
could write:

  let str = state.flatMap {
      switch $0 {
          case .Cold: return "Too cold"
          case .Hot: return "Too hot"
          default: return "now it's just boring"
      }
  }

This would be purely a library solution which is a lot easier to implement
and has the advantage of then giving real use data to see if people
actually use the facility.

···

On Sunday, 31 January 2016, Craig Cruden via swift-evolution < swift-evolution@swift.org> wrote:

The case partial functions are available in all closures, plus you still
have the option to write your syntax based on the proposal with the only
difference being the keyword of match vs switch.

let str = match(state) {
case .Cold: “Too cold”
case .Hot: “Too hot”
default: “now it is just boring”
}

match just being a function that takes a value and a closure.

On 2016-01-31, at 2:11:20, Radosław Pietruszewski via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

I certainly would prefer if we could use switch (and if/else for that
matter) as an expression, instead of some magic syntax usable in `map`. so:

let str = switch(state) {
        case .Cold: "Too cold"
        case .Hot: "Too hot"
        default: "Just right"
    }

— Radek

On 29 Jan 2016, at 08:10, Paul Cantrell via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

On Jan 29, 2016, at 1:01 AM, Charles Constant via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

I'm still +1 on this.

I would very much enjoy being able to do this:

let str = state.map {
  case .Cold: “Too cold”
  case .Hot: “Too hot”
  default: “Just right”
}

The language currently allows this:

    let str: String
    switch(state) {
        case .Cold: str = "Too cold"
        case .Hot: str = "Too hot"
        default: str = "Just right"
    }

While that’s slightly noisier, I’m not convinced the minor additional
concision justifies the substantial additional language complexity. I’m not
even convinced the new syntax is clearer so much as cleverer.

I’d need to see a much more compelling example to be in favor of this
proposal.

Cheers,

Paul

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
<javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
  -- Howard.


(Craig Cruden) #17

I will wait another 24 hours before resubmitting it (even though this discussion thread is not long)….

Anyone that had commented on this in the other thread, but not this one — it would be greatly appreciated if at least one comment (yay or nay) were added to this thread.

I think the last thread where this was discussed for at least 10 days and had many more comments - already fleshed everything out.

···

On 2016-02-03, at 13:03:18, Paul Ossenbruggen <possen@gmail.com> wrote:

Agreed. I really would like this to move forward.

On Feb 2, 2016, at 6:59 PM, Denis Nikitenko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me - I would like to see this discussion continue. Swift already recognizes the utility of pattern matching for initialization by retaining the ?: operator. It would be nice to see a more general and flexible solution that is still reasonably concise and doesn’t introduce new keywords and operators - though I’m sure at least some changes to the language would be inevitable.

On Jan 29, 2016, at 1:43 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

<snip>

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


(David Owens II) #18

I'm really confused by this proposal. Primarily it really seems to be asking for two things:

  1. Allow closures to skip the "switch item" line, and
  2. Implicit returns within closures

The desired troy example:

let weightKg = weightTroy.reduce(0.00) {
    case (let acc, Troy.Pound(let quantity)): acc + Double(quantity) * 0.373
    case (let acc, Troy.Ounce(let quantity)): acc + Double(quantity) * 0.031103
    case (let acc, Troy.Pennyweight(let quantity)): acc + Double(quantity) * 0.001555
    case (let acc, Troy.Grain(let quantity)): acc + Double(quantity) * 0.0000648
}

What you can already do today:

let weightKg = weightTroy.reduce(0.00) { acc, troy in
    switch troy {
    case let .Pound(quantity): return acc + Double(quantity) * 0.373
    case let .Ounce(quantity): return acc + Double(quantity) * 0.031103
    case let .Pennyweight(quantity): return acc + Double(quantity) * 0.001555
    case let .Grain(quantity): return acc + Double(quantity) * 0.0000648
    }
}

Unless I'm misunderstanding some other great value, this seems like a lot of syntax to save typing "switch troy" and "return". If you forget "return", the compiler will already error for you. I don't think the desired example brings more clarity either.

As to the initialization example:

let str: String = {
    switch($0) {
    case .Cold: return "Too cold"
    case .Hot: return "Too hot"
    default: return "Just right"
    }
}(state)

Yes, the type must be explicit because Swift cannot figure it out. I'd rather address that issue.

For me, I'm really not seeing the value the complexity of the proposal brings.

-David

···

On Feb 2, 2016, at 10:07 PM, Craig Cruden via swift-evolution <swift-evolution@swift.org> wrote:

I will wait another 24 hours before resubmitting it (even though this discussion thread is not long)….

Anyone that had commented on this in the other thread, but not this one — it would be greatly appreciated if at least one comment (yay or nay) were added to this thread.

I think the last thread where this was discussed for at least 10 days and had many more comments - already fleshed everything out.

On 2016-02-03, at 13:03:18, Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:

Agreed. I really would like this to move forward.

On Feb 2, 2016, at 6:59 PM, Denis Nikitenko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me - I would like to see this discussion continue. Swift already recognizes the utility of pattern matching for initialization by retaining the ?: operator. It would be nice to see a more general and flexible solution that is still reasonably concise and doesn’t introduce new keywords and operators - though I’m sure at least some changes to the language would be inevitable.

On Jan 29, 2016, at 1:43 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

<snip>

_______________________________________________
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


(Paul Cantrell) #19

I agree completely with this analysis. Spot on.

I'm really confused by this proposal. Primarily it really seems to be asking for two things:

  1. Allow closures to skip the "switch item" line, and
  2. Implicit returns within closures

…plus (3) overloading “map” to operator on single objects and not just collections.

The desired troy example:

let weightKg = weightTroy.reduce(0.00) {
    case (let acc, Troy.Pound(let quantity)): acc + Double(quantity) * 0.373
    case (let acc, Troy.Ounce(let quantity)): acc + Double(quantity) * 0.031103
    case (let acc, Troy.Pennyweight(let quantity)): acc + Double(quantity) * 0.001555
    case (let acc, Troy.Grain(let quantity)): acc + Double(quantity) * 0.0000648
}

What you can already do today:

let weightKg = weightTroy.reduce(0.00) { acc, troy in
    switch troy {
    case let .Pound(quantity): return acc + Double(quantity) * 0.373
    case let .Ounce(quantity): return acc + Double(quantity) * 0.031103
    case let .Pennyweight(quantity): return acc + Double(quantity) * 0.001555
    case let .Grain(quantity): return acc + Double(quantity) * 0.0000648
    }
}

Unless I'm misunderstanding some other great value, this seems like a lot of syntax to save typing "switch troy" and "return”.

Bingo.

···

On Feb 3, 2016, at 12:39 AM, David Owens II via swift-evolution <swift-evolution@swift.org> wrote:

If you forget "return", the compiler will already error for you. I don't think the desired example brings more clarity either.

As to the initialization example:

let str: String = {
    switch($0) {
    case .Cold: return "Too cold"
    case .Hot: return "Too hot"
    default: return "Just right"
    }
}(state)

Yes, the type must be explicit because Swift cannot figure it out. I'd rather address that issue.

For me, I'm really not seeing the value the complexity of the proposal brings.

-David

On Feb 2, 2016, at 10:07 PM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I will wait another 24 hours before resubmitting it (even though this discussion thread is not long)….

Anyone that had commented on this in the other thread, but not this one — it would be greatly appreciated if at least one comment (yay or nay) were added to this thread.

I think the last thread where this was discussed for at least 10 days and had many more comments - already fleshed everything out.

On 2016-02-03, at 13:03:18, Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:

Agreed. I really would like this to move forward.

On Feb 2, 2016, at 6:59 PM, Denis Nikitenko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me - I would like to see this discussion continue. Swift already recognizes the utility of pattern matching for initialization by retaining the ?: operator. It would be nice to see a more general and flexible solution that is still reasonably concise and doesn’t introduce new keywords and operators - though I’m sure at least some changes to the language would be inevitable.

On Jan 29, 2016, at 1:43 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

<snip>

_______________________________________________
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


(Craig Cruden) #20

A switch statement is a a flow-control construct which could also be viewed as a more limited implementation of switch expression that only allows the return type of void/Unit. And the only way to make the switch statement useful is to program by virtue of side-effects - such as mutating a variable. The fact that Swift has a special allowance to set a ‘let’ to nil, then allow it to be set once again to another value as a hack for the most part because of limitations to the fact that it contains flow-control elements with where expressions / functions would allow for the same thing to be done by programming without side-effects.

For example, “switch” could be viewed as

func switch<T,U>(x: T, mapping: T -> U) -> Void {
    return mapping(x)
}

where the only way to do anything useful is to implement the closure containing something that causes side-effects.

Functions/closures etc. foundations come from functional programming constructs, and like those constructs there is a mathematical concept allowing for the defining of a function f(X’) -> Y where X’ is maps only part of the set of values in the set X.

Examples of partial functions are the function square root WHERE square root is restricted to Integers. If you applied that function to (1, 2, 4, 5, 7, 16), the implementation of that function would apply to a subset (1,4, 16) while the function applied to (2,5,7) would be undefined. I am however not suggesting that we implement partial functions in general since for the most part the general implementation is of limited use and can easily implemented using Optionals or ‘Either’. In this case the function closure would be defined through cases (restrictions / defining subsets):

sudo code:
case x is Integer where x is a perfect square : Some(square root of x)
default : None

That is of course a very simple mathematical example of a partial function that is limited by the nature of the types themselves.

Swift at it’s core is not and likely will never be a programming language that fully supports the functional paradigm, but some simple things that like not forcing programmers to program with flow-control and side-effects can make it easier for those that wish to program functionally to at least extend the language through functional libraries.

There has been a lot of interest in allowing the Swift language to implement switch-expressions rather being forced as a hack to use switch flow control statements. This proposal allows for that (using match function) while allowing for a more general implementation that allows for greater flexibility.

Flow-Control statements often are implemented from the viewpoint of for certain conditions - execute this block, for other conditions, this block… which leads to the blocks becoming overloaded and doing many things since if you have one flow control statement that covers it then everything gets implemented in one block rather than having two flow control statements. The functional concept of it is that it is just a function and given a function f(x) you will get y (pure function) where you can test that code and know that no side effects, no state will cause a different result. This allows for very finite test (and reasoning) to prove that a function is correct or not. Programming by flow-control and side-effects ultimately leads you to more complexity and less ability to be sure that a testing will fully cover all cases all branches of the code - which results in less software quality.

···

On 2016-02-04, at 11:40:42, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

I agree completely with this analysis. Spot on.

On Feb 3, 2016, at 12:39 AM, David Owens II via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I'm really confused by this proposal. Primarily it really seems to be asking for two things:

  1. Allow closures to skip the "switch item" line, and
  2. Implicit returns within closures

…plus (3) overloading “map” to operator on single objects and not just collections.

The desired troy example:

let weightKg = weightTroy.reduce(0.00) {
    case (let acc, Troy.Pound(let quantity)): acc + Double(quantity) * 0.373
    case (let acc, Troy.Ounce(let quantity)): acc + Double(quantity) * 0.031103
    case (let acc, Troy.Pennyweight(let quantity)): acc + Double(quantity) * 0.001555
    case (let acc, Troy.Grain(let quantity)): acc + Double(quantity) * 0.0000648
}

What you can already do today:

let weightKg = weightTroy.reduce(0.00) { acc, troy in
    switch troy {
    case let .Pound(quantity): return acc + Double(quantity) * 0.373
    case let .Ounce(quantity): return acc + Double(quantity) * 0.031103
    case let .Pennyweight(quantity): return acc + Double(quantity) * 0.001555
    case let .Grain(quantity): return acc + Double(quantity) * 0.0000648
    }
}

Unless I'm misunderstanding some other great value, this seems like a lot of syntax to save typing "switch troy" and "return”.

Bingo.

If you forget "return", the compiler will already error for you. I don't think the desired example brings more clarity either.

As to the initialization example:

let str: String = {
    switch($0) {
    case .Cold: return "Too cold"
    case .Hot: return "Too hot"
    default: return "Just right"
    }
}(state)

Yes, the type must be explicit because Swift cannot figure it out. I'd rather address that issue.

For me, I'm really not seeing the value the complexity of the proposal brings.

-David

On Feb 2, 2016, at 10:07 PM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I will wait another 24 hours before resubmitting it (even though this discussion thread is not long)….

Anyone that had commented on this in the other thread, but not this one — it would be greatly appreciated if at least one comment (yay or nay) were added to this thread.

I think the last thread where this was discussed for at least 10 days and had many more comments - already fleshed everything out.

On 2016-02-03, at 13:03:18, Paul Ossenbruggen <possen@gmail.com <mailto:possen@gmail.com>> wrote:

Agreed. I really would like this to move forward.

On Feb 2, 2016, at 6:59 PM, Denis Nikitenko via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

+1 from me - I would like to see this discussion continue. Swift already recognizes the utility of pattern matching for initialization by retaining the ?: operator. It would be nice to see a more general and flexible solution that is still reasonably concise and doesn’t introduce new keywords and operators - though I’m sure at least some changes to the language would be inevitable.

On Jan 29, 2016, at 1:43 AM, Craig Cruden via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

The following proposal apparently was not considered widely enough discussed since it was “buried” in a 400 message discussion thread that meandered before coming to the final draft.

As such, to have it reopened — I am restarting the discussion in a new thread to ensure wider discussion.

https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

Pattern Matching Partial Function

<snip>

_______________________________________________
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