[Pitch] Reimagining guard case/if case


(Erica Sadun) #1

Pitch: I'd like to simplify `if case`/`guard case` to drop `case` and replace the equal sign with the pattern matching (`~=`) operator. I think it's simpler, reads better, and emphasizes "this is pattern matching" in a way the current grammar does not.

gist: https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead

Simplifying guard case/if case syntax

Proposal: TBD
Author: Erica Sadun <https://github.com/erica>
Status: TBD
Review manager: TBD

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#introduction>Introduction

This proposal simplifies guard case and if case grammar. It drops the case keyword and replaces the assignment sign with the pattern matching (~=) operator. The results are simpler, they reads better, and it transfers the responsibility of saying "this is pattern matching" from case to ~=.

Swift-evolution thread: [Pitch] Reimagining guard case/if case <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161024/tbd.html>
<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#motivation>Motivation

Swift's guard case and if case statements stand out for their unintuitive approach. They look like assignment statements but they are not assignment statements. They present difficulties for new language adopters because they combine several concepts in a confusing form. They are arguably underutilized by language experts.

Both guard case and `if case statements perform simultaneous pattern matching and conditional binding. Here are examples demonstrating their use in current Swift:

enum Result<T> { case success(T), error(Error) }

// valid Swift
guard case let .success(value) = result
    else { ... }

// valid Swift
guard case .success(let value) = result
    else { ... }
The status quo is iteratively built up in this fashion:

= performs assignment
let x = performs binding
if let x = performs conditional binding
if case .foo(let x) = performs conditional binding and pattern matching
When using if case/guard case in the absense of conditional binding, it duplicates basic pattern matching but uses less obvious semantics. These two statements are functionally identical:

if range ~= myValue { ... } // simpler
if case range = myValue { ... } // confusing
The problems with guard case and if case include:

The = operator looks like assignment and not like pattern matching (~=).
The case layout is both too close to a switch's case but doesn't follow its syntax. In switch, a case is followed by a colon, not an equal sign.
Using the case syntax is unneccessarily wordy. It incorporates case, =, and optionally let/var assignments.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#detailed-design>Detailed Design

This proposal replaces the current syntax with a simpler grammar that prioritizes pattern matching but mirrors basic conditional binding. The new syntax drops the case keyword and replaces = with ~=. The results look like this:

guard let .success(value) ~= result { ... }
guard .success(let value) ~= result { ... }
if let .success(value) ~= result { ... }
if .success(let value) ~= result { ... }
guard let x? ~= anOptional { ... }
if let x? ~= anOptional { ... }
In this update:

The case keyword is subsumed into the (existing) pattern matching operator
The statements adopt the existing if-let and guard-let syntax, including Optional syntactic sugar.
if let x = anOptional { ... } // current
if case let x? = anOptional { ... } // current, would be removed

if let x? ~= anOptional { ... } // proposed replacement for `if case`
On adopting this syntax, the two identical range tests naturally unify to this single version:

if range ~= myValue { ... } // before
if case range = myValue { ... } // before

if range ~= myValue { ... } // after
Using pattern matching without conditional binding naturally simplifies to a standalone Boolean condition clause.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#impact-on-existing-code>Impact on Existing Code

This proposal is breaking and would require migration.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#alternatives-considered>Alternatives Considered

Leaving the grammar as-is, albeit confusing
Retaining case and replacing the equal sign with ~= (pattern matching) or : (to match the switch statement).


(Jay) #2

Hey Erica,

It's not clear in the examples, but are you proposing to drop the 'else'
from guard too?

I hope not, because 'guard' as a keyword is slightly confusing as it is,
the 'else' is the only part of it that makes it clear.

In English you generally want to guard "against" something, so guard reads
a bit like:
guard [against] x == 0 else {do this}
Which doesn't make sense, what it really means is:
guard [that] x == 0, else {do this}

I think that "ensure" would have been a better word:
ensure [that] x == 0 else {do this}

Anyway - hopefully this is just an oversight.

···

On Mon, 24 Oct 2016 at 17:25 Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

Pitch: I'd like to simplify `if case`/`guard case` to drop `case` and
replace the equal sign with the pattern matching (`~=`) operator. I think
it's simpler, reads better, and emphasizes "this is pattern matching" in a
way the current grammar does not.

gist: https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead

Simplifying guard case/if case syntax

   - Proposal: TBD
   - Author: Erica Sadun <https://github.com/erica>
   - Status: TBD
   - Review manager: TBD

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#introduction>
Introduction

This proposal simplifies guard case and if case grammar. It drops the case keyword
and replaces the assignment sign with the pattern matching (~=) operator.
The results are simpler, they reads better, and it transfers the
responsibility of saying "this is pattern matching" from case to ~=.

Swift-evolution thread: [Pitch] Reimagining guard case/if case
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161024/tbd.html>
<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#motivation>
Motivation

Swift's guard case and if case statements stand out for their unintuitive
approach. They look like assignment statements but they are not assignment
statements. They present difficulties for new language adopters because
they combine several concepts in a confusing form. They are arguably
underutilized by language experts.

Both guard case and `if case statements perform simultaneous pattern
matching and conditional binding. Here are examples demonstrating their use
in current Swift:

enum Result<T> { case success(T), error(Error) }

// valid Swift
guard case let .success(value) = result
    else { ... }

// valid Swift
guard case .success(let value) = result
    else { ... }

The status quo is iteratively built up in this fashion:

   - = performs assignment
   - let x = performs binding
   - if let x = performs conditional binding
   - if case .foo(let x) = performs conditional binding *and* pattern
   matching

When using if case/guard case in the absense of conditional binding, it
duplicates basic pattern matching but uses less obvious semantics. These
two statements are functionally identical:

if range ~= myValue { ... } // simpler
if case range = myValue { ... } // confusing

The problems with guard case and if case include:

   - The = operator looks like assignment and not like pattern matching (
   ~=).
   - The case layout is both too close to a switch's case but doesn't
   follow its syntax. In switch, a case is followed by a colon, not an
   equal sign.
   - Using the case syntax is unneccessarily wordy. It incorporates case,
   =, and optionally let/var assignments.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#detailed-design>Detailed
Design

This proposal replaces the current syntax with a simpler grammar that
prioritizes pattern matching but mirrors basic conditional binding. The new
syntax drops the case keyword and replaces = with ~=. The results look
like this:

guard let .success(value) ~= result { ... }
guard .success(let value) ~= result { ... }
if let .success(value) ~= result { ... }
if .success(let value) ~= result { ... }
guard let x? ~= anOptional { ... }
if let x? ~= anOptional { ... }

In this update:

   - The case keyword is subsumed into the (existing) pattern matching
   operator
   - The statements adopt the existing if-let and guard-let syntax,
   including Optional syntactic sugar.

if let x = anOptional { ... } // current
if case let x? = anOptional { ... } // current, would be removed

if let x? ~= anOptional { ... } // proposed replacement for `if case`

On adopting this syntax, the two identical range tests naturally unify to
this single version:

if range ~= myValue { ... } // before
if case range = myValue { ... } // before

if range ~= myValue { ... } // after

Using pattern matching without conditional binding naturally simplifies to
a standalone Boolean condition clause.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#impact-on-existing-code>Impact
on Existing Code

This proposal is breaking and would require migration.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#alternatives-considered>Alternatives
Considered

   - Leaving the grammar as-is, albeit confusing
   - Retaining case and replacing the equal sign with ~= (pattern
   matching) or : (to match the switch statement).

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


(Martin Waitz) #3

Hi,

When using a pattern match operator, I’d prefer to reverse its arguments:

    if value matches pattern …

    if result =~ .success(let x) { use(x) }

Being used to pattern matching in functional languages, I also do like our current syntax.
Using ~= together with `let` on the left looks very strange to me.

— Martin


(Rien) #4

I have tried to like this because at first it seems like a good idea.
But having let it shimmer for a while, I simply cannot muster the enthusiasm for it. Using “~=“ does not ‘feel’ right to me. Especially for a code breaking change at this stage. There are probably already a number of user defined functions out there that overload “~=“.
-1. Sorry.

If possible, I think it would be nice to make the “case” after the “if” or “guard” optional. But that would be enough imo.
Alternatively replacing the “~=“ with a different keyword (and dropping the “case”) could also do trick, as Haravikk suggested (I do like the “matches”).

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

···

On 24 Oct 2016, at 18:24, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

Pitch: I'd like to simplify `if case`/`guard case` to drop `case` and replace the equal sign with the pattern matching (`~=`) operator. I think it's simpler, reads better, and emphasizes "this is pattern matching" in a way the current grammar does not.

gist: https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead

Simplifying guard case/if case syntax

  • Proposal: TBD
  • Author: Erica Sadun
  • Status: TBD
  • Review manager: TBD

Introduction

This proposal simplifies guard case and if case grammar. It drops the case keyword and replaces the assignment sign with the pattern matching (~=) operator. The results are simpler, they reads better, and it transfers the responsibility of saying "this is pattern matching" from case to ~=.

Swift-evolution thread: [Pitch] Reimagining guard case/if case

Motivation

Swift's guard case and if case statements stand out for their unintuitive approach. They look like assignment statements but they are not assignment statements. They present difficulties for new language adopters because they combine several concepts in a confusing form. They are arguably underutilized by language experts.

Both guard case and `if case statements perform simultaneous pattern matching and conditional binding. Here are examples demonstrating their use in current Swift:

enum Result<T> { case success(T), error(Error) }

// valid Swift
guard case let .success(value) = result
    else { ... }

// valid Swift
guard case .success(let value) = result
    else { ... }

The status quo is iteratively built up in this fashion:

  • = performs assignment
  • let x = performs binding
  • if let x = performs conditional binding
  • if case .foo(let x) = performs conditional binding and pattern matching
When using if case/guard case in the absense of conditional binding, it duplicates basic pattern matching but uses less obvious semantics. These two statements are functionally identical:

if range ~= myValue { ... } // simpler
if case range = myValue { ... } // confusing

The problems with guard case and if case include:

  • The = operator looks like assignment and not like pattern matching (~=).
  • The case layout is both too close to a switch's case but doesn't follow its syntax. In switch, a case is followed by a colon, not an equal sign.
  • Using the case syntax is unneccessarily wordy. It incorporates case, =, and optionally let/var assignments.

Detailed Design

This proposal replaces the current syntax with a simpler grammar that prioritizes pattern matching but mirrors basic conditional binding. The new syntax drops the case keyword and replaces = with ~=. The results look like this:

guard let .success(value) ~= result { ... }
guard .success(let value) ~= result { ... }
if let .success(value) ~= result { ... }
if .success(let value) ~= result { ... }
guard let x? ~= anOptional { ... }
if let x? ~= anOptional { ... }

In this update:

  • The case keyword is subsumed into the (existing) pattern matching operator
  • The statements adopt the existing if-let and guard-let syntax, including Optional syntactic sugar.
if let x = anOptional { ... } // current
if case let x? = anOptional { ... } // current, would be removed

if let x? ~= anOptional { ... } // proposed replacement for `if case`

On adopting this syntax, the two identical range tests naturally unify to this single version:

if range ~= myValue { ... } // before
if case range = myValue { ... } // before

if range ~= myValue { ... } // after

Using pattern matching without conditional binding naturally simplifies to a standalone Boolean condition clause.

Impact on Existing Code

This proposal is breaking and would require migration.

Alternatives Considered

  • Leaving the grammar as-is, albeit confusing
  • Retaining case and replacing the equal sign with ~= (pattern matching) or : (to match the switch statement).

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


(Brent Royal-Gordon) #5

This proposal replaces the current syntax with a simpler grammar that prioritizes pattern matching but mirrors basic conditional binding. The new syntax drops the case keyword and replaces = with ~=. The results look like this:

guard let .success(value) ~= result { ... }
guard .success(let value) ~= result { ... }
if let .success(value) ~= result { ... }
if .success(let value) ~= result { ... }
guard let x? ~= anOptional { ... }
if let x? ~= anOptional { ... }

I don't mind this syntax, but I'm not convinced this is worth doing if it's merely a syntax change. (It goes without saying that this is not phase 1.) But if it were motivated by a broader change, I think I could get behind it.

Suppose we introduced this concept:

  protocol Pattern {
    associatedtype Target
    associatedtype Bindings
    
    static func ~= (pattern: Self, target: Target) -> Bindings?
  }
  
  // Sometimes you just want the pattern on the other side.
  func =~ <PatternType: Pattern>(target: PatternType.Target, pattern: PatternType) -> PatternType.Bindings? {
    return pattern ~= target
  }

And further suppose that conditionals were altered to support `~=` and `=~` instead of `case =`. That is, a conditional with a pattern match in it would take the "then" branch if the `~=` returned `.some`, and would bind the variables in the returned tuple. In essence, this:

  if pattern ~= target { … }

Would be rewritten to:

  if let ([variables from pattern]) = (pattern ~= target) { … }

(One way to do this would be to support *any* Optional as a conditional, not just the results of a pattern match; this would essentially make `nil` into a false-ish value. But we tried that in the Swift 1 betas and didn't seem too happy with it.)

Enum cases would then have a dual nature; they could construct values, but they could also construct `Case` instances:

  let value = Foo?.some(Foo())
  // value: Foo?
  
  let pattern1 = Foo?.some(let value) as Case
  // pattern1: Case<Foo?, (value: Foo)>
  
  let pattern2 = Foo?.some(_) as Case
  // pattern2: Case<Foo?, (Foo)>
  
  let pattern3 = Foo?.some as Case
  // pattern3: Case<Foo?, (Foo)>
  
  let aFoo = Foo()
  let pattern4 = Foo?.some(aFoo) as Case
  // pattern4: Case<Foo?, (Foo)>

Note that all four patterns are some variant of `Case<Foo?, (Foo)>`; it's just a matter of re-labeling the second parameter. Hopefully you could do that with a cast:

  if (pattern as Case<Foo?, (foo: Foo)>) ~= optionalFoo {
    // use `foo` here
  }
  
  // Or with more powerful existentials:
  if (pattern as Pattern where Bindings == (foo: Foo)) ~= fooTarget {
    // use `foo` here
  }
  
  // Perhaps some sugar:
  if fooTarget =~ pattern as let value {
    // use `foo` here
  }

Elements with a label are bound to a variable by that name; elements with no label are not bound to any variable.

`Case` would look something like this, with the actual implementation of `~=` being compiler magic:

  class Case<Enum, Associated>: Pattern {
    typealias Target = Enum
    typealias Bindings = Associated
    
    static func ~= (pattern: Case, target: Target) -> Bindings? { … }
  }

(I suppose it might instead be `Enum.Case<Associated>`—just depends on whether we want it to be magic or standard library-ish.)

So, what other patterns could we implement? Well, most notably, regular expressions:

  class Regex<Captures>: Pattern {
    typealias Target = String
    typealias Bindings = #concatenateTuples(($0: String), Captures)
    
    static func ~= (pattern: RegularExpression, target: String) -> Bindings? { … }
  }

You would use them like this:

  if /ab(.(.)?)c/ ~= myString {
    // That's a Regex<($1: String, $2: String?)>.
    // A named capture syntax would allow you to rename $1 and up, or you could cast
    // a regex you had received.
    // If string slicing is corrected so that substrings share indices with their parent string,
    // you would not need a separate "get range of match" feature.
  }

But here's another neat use case:

  let gregorian = Calendar(identifier: .gregorian)
  if myDate =~ DateComponents(calendar: gregorian, month: 10, day: 31) {
    print(":ghost::jack_o_lantern::spider::coffin::chocolate_bar:")
  }
  
  extension DateComponents {
    typealias Target = Date
    typealias Bindings = (DateComponents) // Optionally bindable
    
    static func ~= (pattern: DateComponents, target: Target) -> Bindings? {
      let calendar = self.calendar ?? Calendar.current
      let timeZone = pattern.timeZone ?? TimeZone.current
      
      guard calendar.date(target, matchesComponents: pattern) else {
        return nil
      }
      
      return calendar.dateComponents(in: timeZone, from: date)
    }
  }

Anyway, just a related thought I had. Sorry for hijacking.

···

--
Brent Royal-Gordon
Architechies


(Anton Zhilin) #6

I'd like to write a proposal for "matches" and wonder if it is in Swift 4
Phase 1 scope? It's purely syntactic, and won't affect ABI.


(Erica Sadun) #7

No I am not. That's a copy/paste error. I have fixed that in the gist. Thank you for the catch.

-- E

···

On Oct 24, 2016, at 10:44 AM, Jay Abbott <jay@abbott.me.uk> wrote:

Hey Erica,

It's not clear in the examples, but are you proposing to drop the 'else' from guard too?


(Jay) #8

Oh... and yeah I like the idea of dropping the 'case' word in this context,
it makes sense.

···

On Mon, 24 Oct 2016 at 17:44 Jay Abbott <jay@abbott.me.uk> wrote:

Hey Erica,

It's not clear in the examples, but are you proposing to drop the 'else'
from guard too?

I hope not, because 'guard' as a keyword is slightly confusing as it is,
the 'else' is the only part of it that makes it clear.

In English you generally want to guard "against" something, so guard reads
a bit like:
guard [against] x == 0 else {do this}
Which doesn't make sense, what it really means is:
guard [that] x == 0, else {do this}

I think that "ensure" would have been a better word:
ensure [that] x == 0 else {do this}

Anyway - hopefully this is just an oversight.

On Mon, 24 Oct 2016 at 17:25 Erica Sadun via swift-evolution < > swift-evolution@swift.org> wrote:

Pitch: I'd like to simplify `if case`/`guard case` to drop `case` and
replace the equal sign with the pattern matching (`~=`) operator. I think
it's simpler, reads better, and emphasizes "this is pattern matching" in a
way the current grammar does not.

gist: https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead

Simplifying guard case/if case syntax

   - Proposal: TBD
   - Author: Erica Sadun <https://github.com/erica>
   - Status: TBD
   - Review manager: TBD

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#introduction>
Introduction

This proposal simplifies guard case and if case grammar. It drops the case keyword
and replaces the assignment sign with the pattern matching (~=) operator.
The results are simpler, they reads better, and it transfers the
responsibility of saying "this is pattern matching" from case to ~=.

Swift-evolution thread: [Pitch] Reimagining guard case/if case
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161024/tbd.html>
<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#motivation>
Motivation

Swift's guard case and if case statements stand out for their unintuitive
approach. They look like assignment statements but they are not assignment
statements. They present difficulties for new language adopters because
they combine several concepts in a confusing form. They are arguably
underutilized by language experts.

Both guard case and `if case statements perform simultaneous pattern
matching and conditional binding. Here are examples demonstrating their use
in current Swift:

enum Result<T> { case success(T), error(Error) }

// valid Swift
guard case let .success(value) = result
    else { ... }

// valid Swift
guard case .success(let value) = result
    else { ... }

The status quo is iteratively built up in this fashion:

   - = performs assignment
   - let x = performs binding
   - if let x = performs conditional binding
   - if case .foo(let x) = performs conditional binding *and* pattern
   matching

When using if case/guard case in the absense of conditional binding, it
duplicates basic pattern matching but uses less obvious semantics. These
two statements are functionally identical:

if range ~= myValue { ... } // simpler
if case range = myValue { ... } // confusing

The problems with guard case and if case include:

   - The = operator looks like assignment and not like pattern matching (
   ~=).
   - The case layout is both too close to a switch's case but doesn't
   follow its syntax. In switch, a case is followed by a colon, not an
   equal sign.
   - Using the case syntax is unneccessarily wordy. It incorporates case,
   =, and optionally let/var assignments.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#detailed-design>Detailed
Design

This proposal replaces the current syntax with a simpler grammar that
prioritizes pattern matching but mirrors basic conditional binding. The new
syntax drops the case keyword and replaces = with ~=. The results look
like this:

guard let .success(value) ~= result { ... }
guard .success(let value) ~= result { ... }
if let .success(value) ~= result { ... }
if .success(let value) ~= result { ... }
guard let x? ~= anOptional { ... }
if let x? ~= anOptional { ... }

In this update:

   - The case keyword is subsumed into the (existing) pattern matching
   operator
   - The statements adopt the existing if-let and guard-let syntax,
   including Optional syntactic sugar.

if let x = anOptional { ... } // current
if case let x? = anOptional { ... } // current, would be removed

if let x? ~= anOptional { ... } // proposed replacement for `if case`

On adopting this syntax, the two identical range tests naturally unify to
this single version:

if range ~= myValue { ... } // before
if case range = myValue { ... } // before

if range ~= myValue { ... } // after

Using pattern matching without conditional binding naturally simplifies to
a standalone Boolean condition clause.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#impact-on-existing-code>Impact
on Existing Code

This proposal is breaking and would require migration.

<https://gist.github.com/erica/1a5ce8a5157158c6400efb550778cead#alternatives-considered>Alternatives
Considered

   - Leaving the grammar as-is, albeit confusing
   - Retaining case and replacing the equal sign with ~= (pattern
   matching) or : (to match the switch statement).

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


(Goffredo Marocchi) #9

Love it Erica :)!!!!

···

Sent from my iPhone

On 24 Oct 2016, at 18:55, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On Oct 24, 2016, at 10:44 AM, Jay Abbott <jay@abbott.me.uk> wrote:

Hey Erica,

It's not clear in the examples, but are you proposing to drop the 'else' from guard too?

No I am not. That's a copy/paste error. I have fixed that in the gist. Thank you for the catch.

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


(Haravikk) #10

That's interesting point, it does kind of make more sense that way round, but I wonder if we were to d that a keyword might be even better than an operate, like:

  if result matches .success(let x) { use(x) }
  if result matches let x? { use(x) }

And so-on? Maybe matches isn't the right keyword; we could even re-use the is keyword for something shorter (and just think of a type as a form of pattern)? I could like the idea of doing:

  if result is let x? { use(x) }

My reasoning being that a keyword makes it much more obvious what's going on as it can read like natural language to convey that it's a form of matching, wheres ~= as an operator still requires some learning if you've not seen something similar in another language.

···

On 24 Oct 2016, at 21:38, Martin Waitz via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

When using a pattern match operator, I’d prefer to reverse its arguments:

   if value matches pattern …

   if result =~ .success(let x) { use(x) }

Being used to pattern matching in functional languages, I also do like our current syntax.
Using ~= together with `let` on the left looks very strange to me.


(Haravikk) #11

Hi,

When using a pattern match operator, I’d prefer to reverse its arguments:

   if value matches pattern …

   if result =~ .success(let x) { use(x) }

Being used to pattern matching in functional languages, I also do like our current syntax.
Using ~= together with `let` on the left looks very strange to me.

That's interesting point, it does kind of make more sense that way round, but I wonder if we were to d that a keyword might be even better than an operate, like:

Should read as; "that's an interesting point, it does kind of make more sense that way round, but I wonder if were to do that if a keyword might be even better than an operator"
i.e- like a sane person who proof-reads e-mails might have written it.

···

On 24 Oct 2016, at 21:55, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 24 Oct 2016, at 21:38, Martin Waitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

  if result matches .success(let x) { use(x) }
  if result matches let x? { use(x) }

And so-on? Maybe matches isn't the right keyword; we could even re-use the is keyword for something shorter (and just think of a type as a form of pattern)? I could like the idea of doing:

  if result is let x? { use(x) }

My reasoning being that a keyword makes it much more obvious what's going on as it can read like natural language to convey that it's a form of matching, wheres ~= as an operator still requires some learning if you've not seen something similar in another language.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jeremy Pereira) #12

I have tried to like this because at first it seems like a good idea.
But having let it shimmer for a while, I simply cannot muster the enthusiasm for it. Using “~=“ does not ‘feel’ right to me. Especially for a code breaking change at this stage. There are probably already a number of user defined functions out there that overload “~=“.
-1. Sorry.

Erica chose ~= because it is already the pattern matching operator in Swift. I think it would be just fine.

If possible, I think it would be nice to make the “case” after the “if” or “guard” optional. But that would be enough imo.
Alternatively replacing the “~=“ with a different keyword (and dropping the “case”) could also do trick, as Haravikk suggested (I do like the “matches”).

I don’t like Haravikk’s suggestion because although he calls it a keyword, "matches” would clearly be an operator and in many discussions the idea of using words as operators has been rejected for practical compilation reasons and readability reasons. The idea of using matches because it reads better in English is bogus because, once an operator becomes widely accepted, people read it as what it is. If you see `a || b`, you don’t read "a pipe pipe b", you read "a or b”. I admit I do read "a == b" as "a equals equals b” but that seems completely natural to me.

Also, reading things in English is not necessarily natural to a lot of programmers.

I am +1 for Erica’s idea, although I also like the =~ modification to turn the value and the pattern around.

···

On 25 Oct 2016, at 08:13, Rien via swift-evolution <swift-evolution@swift.org> wrote:


(Haravikk) #13

I'm inclined to disagree; the keyword wouldn't be much different from the use of the is keyword to test a type (I even suggested using it since it's shorter than matches and wouldn't require a new term), both are run-time operations except where they can be optimised away so there is some precedent for this.

On the issue of a || b that's a little different; while personally I would actually like "and" and "or" to be keywords, the operators for these are very common (practically universal), but many languages lack a pattern matching operator, and I don't recall using one that functions quite like Swift's does. Also, I haven't delved into the implementation but is it even really an operator? It doesn't seem like something I could implement myself as I can't define a mechanism for conditional binding etc., so it doesn't really seem like an operator in the traditional sense anyway.

Lastly, on it being more readable the issue isn't general readability but rather discoverability. If you see "if x matches .some(let y)" or "if x is let y?" then there's some immediate context for what's going on the first time you see it. An =~ or ~= operator is less clear as it looks like a weird assignment operator (like +=), doesn't mean the same thing as the tilde operator on its own; the only clue to it being a comparison is that it's used in an if condition.

I dunno, although I've started using the pattern matching operator, I just don't like it.

···

On 25 Oct 2016, at 12:44, Jeremy Pereira via swift-evolution <swift-evolution@swift.org> wrote:

If possible, I think it would be nice to make the “case” after the “if” or “guard” optional. But that would be enough imo.
Alternatively replacing the “~=“ with a different keyword (and dropping the “case”) could also do trick, as Haravikk suggested (I do like the “matches”).

I don’t like Haravikk’s suggestion because although he calls it a keyword, "matches” would clearly be an operator and in many discussions the idea of using words as operators has been rejected for practical compilation reasons and readability reasons. The idea of using matches because it reads better in English is bogus because, once an operator becomes widely accepted, people read it as what it is. If you see `a || b`, you don’t read "a pipe pipe b", you read "a or b”. I admit I do read "a == b" as "a equals equals b” but that seems completely natural to me.


(Karl) #14

-1. I'm not enthusiastic about using ~=. IMO it's only a small improvement over the "if case let..." hieroglyphics and still too cryptic.
  
Like the others, I really like "matches" or some other, more explicit, keyword.
  
Anything more general around pattern-matching should probably wait until we start adding language support for regexes and other complex patterns. Then we can make something that is more logically consistent.

···

  
On Oct 24, 2016 at 10:55 pm, <Haravikk via swift-evolution (mailto:swift-evolution@swift.org)> wrote:
  
>
> On 24 Oct 2016, at 21:38, Martin Waitz via swift-evolution <swift-evolution@swift.org (mailto:swift-evolution@swift.org)> wrote:
>
>
>
> Hi,
>
> When using a pattern match operator, I’d prefer to reverse its arguments:
>
> if value matches pattern …
>
> if result =~ .success(let x) { use(x) }
>
> Being used to pattern matching in functional languages, I also do like our current syntax.
> Using ~= together with `let` on the left looks very strange to me.
  
That's interesting point, it does kind of make more sense that way round, but I wonder if we were to d that a keyword might be even better than an operate, like:
  
if result matches .success(let x) { use(x) }
  
if result matches let x? { use(x) }
  
And so-on? Maybe matches isn't the right keyword; we could even re-use the is keyword for something shorter (and just think of a type as a form of pattern)? I could like the idea of doing:
  
if result is let x? { use(x) }
  
My reasoning being that a keyword makes it much more obvious what's going on as it can read like natural language to convey that it's a form of matching, wheres ~= as an operator still requires some learning if you've not seen something similar in another language.
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org (mailto:swift-evolution@swift.org) https://lists.swift.org/mailman/listinfo/swift-evolution


(Rien) #15

Bingo!, you just formulated what I felt but could not put in words just yet.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

···

On 25 Oct 2016, at 14:15, Haravikk <swift-evolution@haravikk.me> wrote:

Lastly, on it being more readable the issue isn't general readability but rather discoverability. If you see "if x matches .some(let y)" or "if x is let y?" then there's some immediate context for what's going on the first time you see it. An =~ or ~= operator is less clear as it looks like a weird assignment operator (like +=), doesn't mean the same thing as the tilde operator on its own; the only clue to it being a comparison is that it's used in an if condition.


(Anton Zhilin) #16

Haravikk, I fully agree with you; before I read your post I thought I’d
write exactly that.
if <expression> matches <pattern>

1.
Another “ideological” reason:
= is just an operator, so lhs = rhs should be an expression, but meaning of
= is overloaded in this context. On the other hand, when you see a keyword,
you can’t mistake if lhs matches rhs for an expression. Analogy with for-in
plays here.

2.
Does this change simplify the syntax enough that we can remove optional
binding?
Is if x matches y? short/simple enough by comparison to if let x = y?
I think yes, because matches is so much clearer.

3.
We can do the same change for for case pattern in, plus eliminate where
clause? How about this:
for x in y, x matches z?, condition, w matches x

let results: [Either<Double, String>]
for item in results, item matches .left(let num), num >= 0 {
    print(num)
}

Looks like it’s closer to logical sequence of actions, than what we
currently have. Now we have “select .left elements, while iterating through
results, and only select those which are >= 0“.

P.S. We can leave where, or even if in this form of for-in.


(Jeremy Pereira) #17

I'm inclined to disagree; the keyword wouldn't be much different from the use of the is keyword to test a type (I even suggested using it since it's shorter than matches and wouldn't require a new term), both are run-time operations except where they can be optimised away so there is some precedent for this.

That’s more an argument for changing the syntax of `is` and `as`, perhaps giving them a function like syntax like `type(of:)` not an argument for introducing more operators that break the rules. It’s all very well following precedent, but when precedent is wrong, let’s not.

Lastly, on it being more readable the issue isn't general readability but rather discoverability. If you see "if x matches .some(let y)" or "if x is let y?" then there's some immediate context for what's going on the first time you see it. An =~ or ~= operator is less clear as it looks like a weird assignment operator (like +=), doesn't mean the same thing as the tilde operator on its own; the only clue to it being a comparison is that it's used in an if condition.

I’m not sure I like compromising readability just so that people who have no Swift experience can understand things a bit more easily.

I dunno, although I've started using the pattern matching operator, I just don't like it.

It’s a bit similar to Perl’s string pattern matching operator (=~), so to me it seems fine and it’s already part of the language.

···

On 25 Oct 2016, at 13:15, Haravikk <swift-evolution@haravikk.me> wrote:


(Marco Pace) #18

Hi,
I agree removing the "case" word after the "if" / "guard", but I prefer removing it completely rather then make it optional to limit the number of different implementation.

Regarding the operator, I agree that it can be improved because we should find something that contains two different operations:
   - check against the pattern
   - assign the value

For me both "is" and "match" miss one of the two part: the "is" keyword doesn't feel like a check against the pattern to me, while the "match" keyboard doesn't contain information regarding the assignation.

+1 for Erika proposal, I like the idea of using "~=", it explicits both operations and it is an operator already used in Swift for a similar use-case.

Marco


(Adrian Kashivskyy) #19

I’m -1 as it’s currently written, for the following reasons:

1. Differences are introduced to pattern matching in different parts of the language (`switch` vs `if`/`guard` vs `for`).

2. Exclusion of `for` in the proposal is either deliberate (which relates to point 1.) or done as a result of a rush, which is not good either. This proposal should include a resolution for `for case let where` clauses.

3. Syntatic sugar of optional matching `let x? ~= maybeX` feels completely out of place and looks nothing like standard optional bindings. I will remain strongly against this change.

– Adrian


(Haravikk) #20

I'm inclined to disagree; the keyword wouldn't be much different from the use of the is keyword to test a type (I even suggested using it since it's shorter than matches and wouldn't require a new term), both are run-time operations except where they can be optimised away so there is some precedent for this.

That’s more an argument for changing the syntax of `is` and `as`, perhaps giving them a function like syntax like `type(of:)` not an argument for introducing more operators that break the rules. It’s all very well following precedent, but when precedent is wrong, let’s not.

Who's to say that the precedent is wrong? is and as are very convenient keywords, far more so than having to learn a symbolic operator that might not be obvious, or digging around for a function, especially when said function would really need to be on the type, thus causing it to read in reverse of what you'd expect.

Keywords have their place, you can't just declare them wrong. Plus as I said, unless there's some hidden magic I'm unaware of pattern matching isn't actually a true operator anyway, it's syntactic sugar that looks like one, so why not a keyword? I think a case has to be made for this to be an operator every bit as much as the case for using a keyword.

Lastly, on it being more readable the issue isn't general readability but rather discoverability. If you see "if x matches .some(let y)" or "if x is let y?" then there's some immediate context for what's going on the first time you see it. An =~ or ~= operator is less clear as it looks like a weird assignment operator (like +=), doesn't mean the same thing as the tilde operator on its own; the only clue to it being a comparison is that it's used in an if condition.

I’m not sure I like compromising readability just so that people who have no Swift experience can understand things a bit more easily.

Compromising readability how? And it's not *just* so that people with no experience can learn; I don't find =~ or ~= that appealing as I don't feel they really communicate their purpose at all, it's just a matter of learning it by rote, which I'd prefer to avoid.

It’s a bit similar to Perl’s string pattern matching operator (=~), so to me it seems fine and it’s already part of the language.

That's missing the point; I don't think I've used Perl once since university, and I can't think of any language I've used in the past five years that has an operator like this. Obviously other people's experience will differ, but the point is that I don't think it's common enough to consider a term of art, and on its own it doesn't convey any meaning. Like I said, tilde already has a meaning for bit-flipping, which this doesn't do, so it's something that must be purely learned, unlike say += which is intuitive if you know what both = and + do.

···

On 28 Oct 2016, at 10:00, Jeremy Pereira <jeremy.j.pereira@googlemail.com> wrote:

On 25 Oct 2016, at 13:15, Haravikk <swift-evolution@haravikk.me> wrote: