Proposal: Pattern Matching Partial Function (#111)

And the only way to make the switch statement useful is to program by virtue of side-effects - such as mutating a variable.

This statement is not true, or maybe I don't really understand what you're saying. There are no side-effects in the code below:

func match<T, U>(matchee: T, mapping: T -> U) -> U {
    return mapping(matchee)
}

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

-David

···

On Feb 3, 2016, at 10:52 PM, Craig Cruden <ccruden@novafore.com> wrote:

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 <mailto: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

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.

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

···

On Feb 3, 2016, at 11:08 PM, David Owens II via swift-evolution <swift-evolution@swift.org> wrote:

And the only way to make the switch statement useful is to program by virtue of side-effects - such as mutating a variable.

This statement is not true, or maybe I don't really understand what you're saying. There are no side-effects in the code below:

func match<T, U>(matchee: T, mapping: T -> U) -> U {
    return mapping(matchee)
}

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

-David

On Feb 3, 2016, at 10:52 PM, Craig Cruden <ccruden@novafore.com <mailto:ccruden@novafore.com>> wrote:

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 <mailto: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

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

Yes, I assume that we understand all of this as a starting point for this discussion. No need to explain what pure functions are. And no need to import the functional vs. imperative holy wars here; they have beaten to death elsewhere!

I like pure functional programming too. And if Swift were Scheme or Haskell, then building switch expressions out of these partial functions would make good sense.

But Swift isn’t, and it doesn’t. Despite its support for functional style, Swift is a language that uses expressions — not lambdas — as its basic building block.

Thus, while this is true:

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)

…it’s putting the cart before the horse in the Swift world. If you took that partial expression + match function approach you’re proposed to build switch expressions, it would be the only feature of Swift where an expression-like construct is built from lambdas rather than the other way around. And it would stick out like a sore thumb.

This is not true:

while allowing for a more general implementation that allows for greater flexibility.

Switch expressions and the “partial function” closures proposed here are trivially equivalent. It’s easy to build one out of the other, and neither offers greater flexibility than the other. It’s just that one is more concise/natural if your code is mostly built out of lambdas, and the other is more concise/natural if it’s mostly expressions.

This is a misdescription of what Swift does:

The fact that Swift has a special allowance to set a ‘let’ to nil, then allow it to be set once again

Semantically, the “let” is not set to _anything_ on any code path where its initialization is not guaranteed. There is no moment when an uninitialized let var is observably nil (unless you start doing unsafe ops on raw memory, in which case all behavioral guarantees go out the window).

Though laid out sequentially, a series of statements which do nothing but initialize one or more let vars are a logically equivalent to a single expression that initializes them. In fact, I believe it’s the case that Swift’s lovely struct semantics are such that if you (1) don’t use classes and (2) only use “let” in your local vars (though “var” is fine on struct properties!), then your is secretly all pure functions — even when it has things that look like assignments.

Fundamentally, this switch expr vs. partial functions debate should not be about whether functional programming is better and everyone should do it, but about what solves problems for Swift programmers in a way that’s consistent with Swift’s programmer model.

Cheers,

Paul

···

On Feb 4, 2016, at 12:52 AM, Craig Cruden <ccruden@novafore.com> wrote:

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 <mailto: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

That is not existing swift code, switch right now is a statement.

What you are implementing there is a hack, trying to shoe-horn a switch statement construct into functional switch/match expression use. And yes, technically when you execute “return” in the middle of a switch statement — it is in fact a flow-control/side effect because “return” is an early return / short circuit that immediately exits in the middle of execution.

You also lose the ability for str to be defined through type inference because of it. Adding to the clutter (in addition to having to implement a switch statement) — you are basically forcing a lot of code boilerplate into it and thus taking away from focusing just what the functionality is.

You are focusing too much on switch / case syntax that you are inferring that a `case` inside a closure is effectively the same thing.

The `case` inside the closure should just be in a method for decomposing and conditionally defining part of a function.

Think of f(x, y | x > 5) as partial function f(x, y) where x > 5
And f(x, y | x <= 5) as a partial function f(x, y) where x <= 5

So instead of defining a total function:

  (x, y) in { …. }

you are defining only a partial of the function in each case:

  case (x, y) where x > 5 in { }
  case (x, y) where <= 5 in { }

each case there is an inferred “orElse” chaining the partial functions into a total function.

Instead of using the functional ‘in’ we are sticking with existing ‘:’ notation since that is what people are use to.

···

On 2016-02-04, at 14:08:13, David Owens II <david@owensd.io> wrote:

And the only way to make the switch statement useful is to program by virtue of side-effects - such as mutating a variable.

This statement is not true, or maybe I don't really understand what you're saying. There are no side-effects in the code below:

func match<T, U>(matchee: T, mapping: T -> U) -> U {
    return mapping(matchee)
}

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

-David

On Feb 3, 2016, at 10:52 PM, Craig Cruden <ccruden@novafore.com <mailto:ccruden@novafore.com>> wrote:

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 <mailto: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

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".

···

Le 4 févr. 2016 à 02:41, Paul Ossenbruggen via swift-evolution <swift-evolution@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.

-
Dany

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

That's easy, just use the match() function from the proposal:

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

No new syntax needed for that.

-Thorsten

···

Am 07.02.2016 um 16:47 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org>:

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" } }

As/if the implicit returns is generalized, I feel that match is bit out of place in the for nested if. I should have provided an example to clarify my thought. Here’s one, where the :? do a better job, but this thread has its origin in came people not liking this sometime cryptic operator (which I like to use)

    str = major > 0 ? "Major" : minor > 0 ? "Minor" : "None"

which is ill suited for switch case but doable.

    switch (major,minor) {
        case (_, _) where major > 0: str="Major"
        case (_, _) where minor > 0: str="Minor"
        default: str="None"
    }

Assuming that the implicit returns is generalized to all closures (and even functions like getter), of course only for the one matching my safety rule (in short single statement/function call/exhaustive if or switch). The if can be express as, using the new global match function:

    str = match(major,minor){ if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

or using normal inline closure calls:

    str = { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }(major,minor)

versus the feeding I wonder about:

    str = (major,minor) -> { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

Maybe it’s just me, but the match function feels a bit weird, in the first version.

Dany

···

Le 8 févr. 2016 à 10:54, Thorsten Seitz <tseitz42@icloud.com> a écrit :

Am 07.02.2016 um 16:47 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

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" } }

That's easy, just use the match() function from the proposal:

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

No new syntax needed for that.

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" } }

That's easy, just use the match() function from the proposal:

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

No new syntax needed for that.

As/if the implicit returns is generalized, I feel that match is bit out of place in the for nested if. I should have provided an example to clarify my thought. Here’s one, where the :? do a better job, but this thread has its origin in came people not liking this sometime cryptic operator (which I like to use)

I like :? too, btw.

    str = major > 0 ? "Major" : minor > 0 ? "Minor" : "None"

which is ill suited for switch case but doable.

No need to press something into switch which does not fit.
The proposal is not about using switch for everything and it is not about replacing the ternary operator. It is more about making a switch expression available.

    switch (major,minor) {
        case (_, _) where major > 0: str="Major"
        case (_, _) where minor > 0: str="Minor"
        default: str="None"
    }

Rewriting a little bit I don't think switch looks that bad even for your example:

switch (major,minor) {
case _ where major > 0: "Major"
case _ where minor > 0: "Minor"
default: "None"
}

Assuming that the implicit returns is generalized to all closures (and even functions like getter), of course only for the one matching my safety rule (in short single statement/function call/exhaustive if or switch). The if can be express as, using the new global match function:

    str = match(major,minor){ if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

Making `if` into an expression (which would be a requirementbhere) has been more or less ruled out by Chris Lattner.

or using normal inline closure calls:

    str = { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }(major,minor)

versus the feeding I wonder about:

    str = (major,minor) -> { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

Maybe it’s just me, but the match function feels a bit weird, in the first version.

I agree. That's why it is accompanied by the partial function closure syntax which effectively results in a switch or rather match expression.

-Thorsten

···

Am 09.02.2016 um 01:51 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org>:

Le 8 févr. 2016 à 10:54, Thorsten Seitz <tseitz42@icloud.com> a écrit :

Am 07.02.2016 um 16:47 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org>:

The ternary expression can also be accomplished by this match:

    str = match((major > 0, minor > 0)) {
        case (true, _): "Major"
        case (_, true): "Minor"
        default: "None"
    }

Or using "cases":

    str = match((major > 0, minor > 0)) {
        cases (true, _): "Major", (_, true): "Minor"
        default: "None"
    }

At least the former version is to me clearer than the original ternary expression.
While the switch expression "forces" you to format the code whereas a ternary operator doesn't.

"correctly" formatted:

    str = major > 0 ? "Major" :
             minor > 0 ? "Minor" : "None"

But as Thorsten has already said the proposal is not about removing the ternary operator.

- Maximilian

···

Am 09.02.2016 um 01:51 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org>:

Le 8 févr. 2016 à 10:54, Thorsten Seitz <tseitz42@icloud.com> a écrit :

Am 07.02.2016 um 16:47 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org>:

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" } }

That's easy, just use the match() function from the proposal:

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

No new syntax needed for that.

As/if the implicit returns is generalized, I feel that match is bit out of place in the for nested if. I should have provided an example to clarify my thought. Here’s one, where the :? do a better job, but this thread has its origin in came people not liking this sometime cryptic operator (which I like to use)

    str = major > 0 ? "Major" : minor > 0 ? "Minor" : "None"

which is ill suited for switch case but doable.

    switch (major,minor) {
        case (_, _) where major > 0: str="Major"
        case (_, _) where minor > 0: str="Minor"
        default: str="None"
    }

Assuming that the implicit returns is generalized to all closures (and even functions like getter), of course only for the one matching my safety rule (in short single statement/function call/exhaustive if or switch). The if can be express as, using the new global match function:

    str = match(major,minor){ if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

or using normal inline closure calls:

    str = { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }(major,minor)

versus the feeding I wonder about:

    str = (major,minor) -> { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

Maybe it’s just me, but the match function feels a bit weird, in the first version.

Dany

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

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" } }

That's easy, just use the match() function from the proposal:

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

No new syntax needed for that.

As/if the implicit returns is generalized, I feel that match is bit out of place in the for nested if. I should have provided an example to clarify my thought. Here’s one, where the :? do a better job, but this thread has its origin in came people not liking this sometime cryptic operator (which I like to use)

I like :? too, btw.

    str = major > 0 ? "Major" : minor > 0 ? "Minor" : "None"

which is ill suited for switch case but doable.

No need to press something into switch which does not fit.
The proposal is not about using switch for everything and it is not about replacing the ternary operator. It is more about making a switch expression available.

Of course, it’s not about what should remain unmentioned (saying it out loud three time may cause that thread to come back and haunt us).
But while the proposal can be mainly read as:

- provide a special case-list closure (aka pattern matching partial function dedicated to switch) and expand implicit returns support for this closure type only.

it could also be read as

- expand the implicit returns support for all closure, allow such closure to skip the switch keyword; be the case list (aka pattern matching partial function).

I do think that safely expanding the implicit returns support in either the dedicated closure or the general closure would be roughly the same.

I’m not sure where I stand on the switch-less case-list closure, but I am afraid that providing such dedicated closure syntax may set a precedent and cause others to want yet another dedicated closure syntax for their own goal; how do we consider a special closure syntax as acceptable, how many of them are too many. This is why, I’m looking at it from a wide angle view; an expanded implicit returns support safely done could benefit other general cases. Doing this as a first step, will miss 50% of this proposal (still need to use switch ($0)), but the overall use case goal will still be achieved; being able to do something like; let something = condition ? data1 : data2; for more complex scenarios.

I’m having trouble following my own train of thought, but I think it’s leading me toward having the "safely expanded implicit returns » portion extracted as its own proposal for general purpose, which this proposal can then rely on.

    switch (major,minor) {
        case (_, _) where major > 0: str="Major"
        case (_, _) where minor > 0: str="Minor"
        default: str="None"
    }

Rewriting a little bit I don't think switch looks that bad even for your example:

switch (major,minor) {
case _ where major > 0: "Major"
case _ where minor > 0: "Minor"
default: "None"
}

Assuming that the implicit returns is generalized to all closures (and even functions like getter), of course only for the one matching my safety rule (in short single statement/function call/exhaustive if or switch). The if can be express as, using the new global match function:

    str = match(major,minor){ if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

Making `if` into an expression (which would be a requirementbhere) has been more or less ruled out by Chris Lattner.

I recall some oppositions early on, but I do not recall if it was before the thread move into closure territory.
Don’t forget my example here as two big if:
- if we generalized the implicit return done safely to any closure.
- if we try to call the closure passing the parameters with the only (to be) available prefix syntax

Dany

···

Le 9 févr. 2016 à 01:00, Thorsten Seitz <tseitz42@icloud.com> a écrit :
Am 09.02.2016 um 01:51 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

Le 8 févr. 2016 à 10:54, Thorsten Seitz <tseitz42@icloud.com <mailto:tseitz42@icloud.com>> a écrit :
Am 07.02.2016 um 16:47 schrieb Dany St-Amant via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

or using normal inline closure calls:

    str = { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }(major,minor)

versus the feeding I wonder about:

    str = (major,minor) -> { if $0 > 0 { "Major" } else if $1 > 0 { "Minor" } else { "None" } }

Maybe it’s just me, but the match function feels a bit weird, in the first version.

I agree. That's why it is accompanied by the partial function closure syntax which effectively results in a switch or rather match expression.