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

T.J. Usiyan griotspeak at gmail.com
Sun Mar 27 04:55:04 CDT 2016


I wrote these a while ago and considered proposing that they be added.
Might as well throw them into this thread since they are another approach
to solving the problem.


``` swift
import Foundation
    // MARK: <
    public func <<T : Comparable>(lhs: T, rhs: T) -> (result: Bool,
value:T) {
        return (lhs < rhs, rhs)
    }

    public func <<T : Comparable>(lhs: (result: Bool, value: T), rhs: T) ->
Bool {
            return lhs.result && (lhs.value < rhs)
        }

    public func <<T : Comparable>(lhs: (Bool, T), rhs: T) -> (Bool, T) {
            return (lhs < rhs, rhs)
        }

// MARK: >
public func ><T : Comparable>(lhs: T, rhs: T) -> (result: Bool, value:T) {
    return (lhs > rhs, rhs)
    }

public func ><T : Comparable>(lhs: (result: Bool, value: T), rhs: T) ->
Bool {
    return lhs.result && (lhs.value > rhs)
    }

public func ><T : Comparable>(lhs: (Bool, T), rhs: T) -> (Bool, T) {
    return (lhs > rhs, rhs)
    }
// MARK: >=
public func >=<T : Comparable>(lhs: T, rhs: T) -> (result: Bool, value:T) {
    return (lhs >= rhs, rhs)
    }

public func >=<T : Comparable>(lhs: (result: Bool, value: T), rhs: T) ->
Bool {
    return lhs.result && (lhs.value >= rhs)
    }

public func >=<T : Comparable>(lhs: (Bool, T), rhs: T) -> (Bool, T) {
    return (lhs >= rhs, rhs)
    }

// MARK: <=
public func <=<T : Comparable>(lhs: T, rhs: T) -> (result: Bool, value:T) {
    return (lhs <= rhs, rhs)
    }

public func <=<T : Comparable>(lhs: (result: Bool, value: T), rhs: T) ->
Bool {
    return lhs.result && (lhs.value <= rhs)
    }

public func <=<T : Comparable>(lhs: (Bool, T), rhs: T) -> (Bool, T) {
    return (lhs <= rhs, rhs)
    }

// MARK: ==
public func ==<T : Comparable>(lhs: T, rhs: T) -> (result: Bool, value:T) {
    return (lhs == rhs, rhs)
    }

public func ==<T : Comparable>(lhs: (result: Bool, value: T), rhs: T) ->
Bool {
    return lhs.result && (lhs.value == rhs)
    }

public func ==<T : Comparable>(lhs: (Bool, T), rhs: T) -> (Bool, T) {
    return (lhs == rhs, rhs)
    }
let value = 5

if (0 < value) < 10 {
    print("in the middle!")
    } else {
    print("nope")
    }

```

On Sun, Mar 27, 2016 at 5:19 AM, Lukas Stabe via swift-evolution <
swift-evolution at swift.org> wrote:
>
> I'd love to see `if value matches pattern`. Every time I use `if case` I
have to stop and think to remember the weird syntax. I get where it came
from, but I think `matches` better describes what happens here (one is
immediately reminded of pattern matching).
>
> I don't know wether this might be unfeasible to do due to compiler
performance limitations, but I would support a proposal to replace `if
case` with `if value matches pattern`.
>
> An alternative may be to introduce a custom operator that just calls `~=`
with the arguments reversed, but imho this should rather be fixed at the
language level.
>
> – Lukas
>
> On 26 Mar 2016, at 18:13, Erica Sadun via swift-users <
swift-users at swift.org> wrote:
>
> 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
>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
>
> _______________________________________________
> 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/20160327/18db3501/attachment.html>


More information about the swift-evolution mailing list