Skip to content

Instantly share code, notes, and snippets.

@erica
Last active October 24, 2016 17:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save erica/1a5ce8a5157158c6400efb550778cead to your computer and use it in GitHub Desktop.
Save erica/1a5ce8a5157158c6400efb550778cead to your computer and use it in GitHub Desktop.

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 else { ... }
guard .success(let value) ~= result else { ... }
if let .success(value) ~= result { ... }
if .success(let value) ~= result { ... }
guard let x? ~= anOptional else { ... }
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.

Excluded from this proposal

This proposal does not address switch case or for case.

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).
@adrfer
Copy link

adrfer commented Oct 24, 2016

@erica, did some minor improvements on this gist. Feel free to take a look at my fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment