[swift-evolution] [swift-users] pattern matching in if improvement?

Erica Sadun erica at ericasadun.com
Sat Mar 26 18:13:55 CDT 2016


> On Mar 26, 2016, at 3:47 PM, Maury Markowitz via swift-users <swift-users at swift.org> wrote:
> 
> Before I stick my head into the other list, consider:
> 
>   if statusCode >= 200 && statusCode <= 299
> 
> I'm sure examples of something like this occur throughout your code. But the actual semantics of the test is hidden here, you're really testing if statusCode lies within a range. Swift 2.0 has a couple of options for this, but I find them all rather odd. The most recommended is:
> 
>   if case 0...100 = someInteger

While I prefer: 

if 200...299 ~= statusCode { print("within 200-299") }

I see that you're asking specifically about case/=. But keep that example in mind because I'm
going to circle back to it.

> This syntax has problems. For one thing, it's written backwards compared to most people's code...
> 
>   if someinteger == 100
> 
> not...
> 
>   if 100 == someinteger
> 
> so it just *feels* wrong. In addition, the use of "case" seems odd too. And finally, there's the use of the single equals sign in a test, which goes against everything we've learned in C-like languages.
> 
> So unless I'm missing something, can anyone offer a reason this wouldn't work?
> 
>  if someinteger in 0...100

This is a built-in problem with "if case/=". Starting with the core statement :

if case pattern = value {...}

It's far easier to read and understand the equivalent switch than the one-liner:

switch (value) {
case pattern: ...
default: break
}

Once you convert to a switch, the "If this value can be matched to this pattern" 
becomes a lot less mentally weird but there's also a lot of extra fluff that if case
attempts to trim away. Here's a concrete example.  "In the case that the pattern 
Test.A(Int) can be matched to this value then bind x to the associated Int value"

let value = Test.A(23)

if case Test.A(let x) = value {
    print(x) // will print 23
}

Again the switch is a lot more intuitive to read, but contains a lot of unneeded
details that can and should be trimmable:

switch (value) {
case Test.A(let x): ...
default: break
}

And here's the oddest example of this Case/= construct I can think of in terms
of the "read through" not matching the actual programming intent of "In the 
case that the array indices can be matched to this value" 

if case array.indices = array.startIndex { print("strange but yup") }

And its switch equivalent, which is way more obvious in terms of intent:

switch (array.startIndex) {
case array.indices: ...
default: break
}

Now back to your original point. Could this be expressed better? For sure. I think these are far more readable:

if value in range {...} // vs if range ~=
if value matches pattern {...} // vs if case pattern = value

And for these specific examples, they'd look like this in an updated Swift that adopted these changes:

if statusCode in 200...299 { print("within 200-299") }
if value matches Test.A(let x) { print(x) } // will print 23
if array.startIndex in array.indices { print("the ~= variation") }
if array.startIndex matches array.indices { print ("better example Case/=") }

That said, I've also made my opinion clear over there that the use of "let" and "var"
in "if let" unnecessarily overloads constant and variable binding (it's testing something 
that actually acts differently than the standalone let due to unwrapping). This got nowhere
for a variety of compelling and less compelling reasons. (I'd prefer "if bind" even if it  
sacrifices a variable variant.)

I certainly think it's worth doing at least a [Pitch] over in -evolution with the alternate 
constructs.

-- E
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160326/d11f7e2f/attachment.html>


More information about the swift-evolution mailing list