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

Paul Ossenbruggen possen at gmail.com
Thu Feb 4 01:41:36 CST 2016


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 at 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 at novafore.com <mailto:ccruden at 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 at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at 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 at gmail.com <mailto:possen at 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 at swift.org <mailto:swift-evolution at 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 at swift.org <mailto:swift-evolution at 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 <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 at swift.org <mailto:swift-evolution at swift.org>
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

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


More information about the swift-evolution mailing list