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

Paul Cantrell cantrell at pobox.com
Thu Feb 4 12:34:08 CST 2016


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

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


More information about the swift-evolution mailing list