# \[Pitch\] is case expressions

**URL:** <https://forums.swift.org/t/pitch-is-case-expressions/64185>\
**Category:** Pitches\
**Created:** [April 4, 2023, 4:03pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185 "2023-04-04T16:03:02Z")\
**Posts on this page:** 20\
**Page:** 3

<div class="post-metadata">

**Author:** ![ksluder](https://avatars.discourse-cdn.com/v4/letter/k/eada6e/32.png) [@ksluder](https://forums.swift.org/u/ksluder)\
**Post date:** [April 5, 2023, 4:48pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/41 "2023-04-05T16:48:11Z")

</div>

I don’t agree with the strategy of using this pitch as a Trojan horse to reinvent Swift’s pattern matching syntax. That seems like a completely separate discussion.

---

<div class="post-metadata">

**Author:** ![stephencelis](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/stephencelis/32/2675_2.png) [@stephencelis](https://forums.swift.org/u/stephencelis)\
**Post date:** [April 5, 2023, 6:40pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/42 "2023-04-05T18:40:35Z")

</div>

The main issues I see with `matches` is that it introduces a new keyword instead of reusing `is` and `case`, and it also makes the path forward to expressive case extraction even more ambiguous, as `is case` at least suggests `as case`.

```swift
destination is case .messageThread // true
destination as case .messageThread // Optional(42)

```

What would the equivalent be for `matches`?

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 5, 2023, 7:02pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/43 "2023-04-05T19:02:45Z")

</div>

Drop `case`?

```swift
destination is .messageThread // true
destination as .messageThread // Optional(42)

```

---

<div class="post-metadata">

**Author:** ![wes1](https://avatars.discourse-cdn.com/v4/letter/w/848f3c/32.png) [@wes1](https://forums.swift.org/u/wes1)\
**Post date:** [April 5, 2023, 7:08pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/44 "2023-04-05T19:08:51Z")

</div>

This is great. It would be really nice to avoid the switch dance when reducing to boolean. (I really appreciate the acknowledgments.)

wrt bikeshedding names, among the "Alternatives considered" is the spelling without `is`:

> // \<expr\> case \<pattern\>  
> // Not evocative of a boolean condition  
> HeaderView(inThread: destination case .messageThread)

I prefer this as being consistent with case patterns. Without `is`, the difference between case patterns and case expressions is that in a pure expression context, variables are not bound.

I don't like "is" because it means virtually anything, encourages the notion that code is to be read aloud to be understood, and requires `case` anyway to make any sense.

I don't like "matches" because there are many kinds of matches, and it only indirectly suggests swift patterns. (Similarly, `=~` is too similar to ...)

By contrast, `case` alone is already a keyword, and clearly restricts the domain to enumerations.

One expression usage is the ternary expression used to simplify a complex case without resorting to the full switch:

```swift
enum Domain {
  case unstated, undermin, min, negative, zero, positive, max, overmax
  func defined() -> Bool? {
    (self case .undermin || self case .overmax || self case .unstated)
      ? nil
        : !(self case .zero)
  }
}

```

I think `is` would make this less clear.

This raises a separate issue, whether case expressions in an enumeration context can elide `self`:

```swift
enum Domain {
  ...
  func defined() -> Bool? {
    (case .undermin || case.overmax || case .unstated)
      ? nil
        : !(case .zero)
  }
}

```

---

<div class="post-metadata">

**Author:** ![GreatApe](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/greatape/32/2640_2.png) [@GreatApe](https://forums.swift.org/u/GreatApe)\
**Post date:** [April 5, 2023, 7:26pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/45 "2023-04-05T19:26:44Z")

</div>

Is case seems weird and clunky, but both this suggestion and matches suffer from the problem that they are leas powerful than an actual discriminant enum for example; you can do _does NOT equal_, nor making an array of discriminants for example. For these reasons I definitely think we should think more about this and come up with a more powerful solution.

For example, you could let case operate on the other side, and turn the unknown instance into its discriminant:

```swift
let isOpen = case currentState == .open

// where currentState is

enum State {
    case open(String)
    case closed(Int)
}

// and case currentState is of this synthesized type

enum State.Case {
    case open
    case closed
}

```

---

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [April 5, 2023, 8:31pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/46 "2023-04-05T20:31:25Z")

</div>

> [@ksluder](#):
>
> I don’t agree with the strategy of using this pitch as a Trojan horse to reinvent Swift’s pattern matching syntax. That seems like a completely separate discussion.

I object to the characterization of a “Trojan horse”. That phrase implies deception and malicious intent.

I am being clear and direct in two goals that I see as desirable:

1. I want to improve this pitch by changing the keyword from `is case` to `matches`.

2. I want to improve Swift’s existing pattern-matching by changing `if case` to `if ... matches`.

* * *

Furthermore, item (2) is _not_ a completely separate discussion from this pitch.

Notably, if this pitch were to be accepted as-is, then that would effectively lock in the spelling `is case`. Perhaps there might be support for a follow-on proposal to enable variable binding in `if ... is case` and thereby supersede `if case`, but it would be essentially impossible to change the keyword from “`is case`” to “`matches`” after the fact.

Thus, the proper spelling of the keyword needs to be decided before any proposal in this space gets accepted.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [April 5, 2023, 8:42pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/47 "2023-04-05T20:42:50Z")

</div>

> [@Nevin](#):
>
> Notably, if this pitch were to be accepted as-is, then that would effectively lock in the spelling `is case`. Perhaps there might be support for a follow-on proposal to enable variable binding in `if ... is case` and thereby supersede `if case`, but it would be essentially impossible to change the keyword from “`is case`” to “`matches`” after the fact.

my feeling is this was already effectively “locked in” many years ago. to rename this keyword and rearrange its syntax would be massively source-breaking, to an extent that the cost of migrating to the new syntax would not be worth the (marginal?) improvement in clarity it would yield.

---

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [April 5, 2023, 9:34pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/48 "2023-04-05T21:34:08Z")

</div>

As I said above, I understand that we cannot actually remove `if case` from the language. But we can introduce a better spelling for it, so no one will want to use `if case` anymore.

The spelling `is case` does not yet exist in the language. It is not locked in. We can choose whether to use that spelling or a different one like `matches`.

---

<div class="post-metadata">

**Author:** ![matthewcheok](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/matthewcheok/32/29773_2.png) [@matthewcheok](https://forums.swift.org/u/matthewcheok)\
**Post date:** [April 6, 2023, 1:36am UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/49 "2023-04-06T01:36:26Z")

</div>

> The spelling `is case` does not yet exist in the language. It is not locked in. We can choose whether to use that spelling or a different one like `matches` .

I do think it is better, for this pitch, to evaluate the utility of an inline pattern matching syntax rather than a divergent keyword change to the language.

I don't think that the addition of `is case` would preclude eventually changing the pattern matching syntax to use `matches`. In fact, **should** we end up wanting to change the pattern matching syntax, we could do a holistic change and change it wherever a pattern may be used (and even consider the `switch` statement like below):

```swift
// inline checks
destination is case .messageThread
destination matches .messageThread

// if condition
if case .messageThread = destination {
if destination matches .messageThread {

// within switch
switch destination {
case .messageThread: ...
matches .messageThread: ...
}

```

---

<div class="post-metadata">

**Author:** ![BrentM](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/brentm/32/15134_2.png) [@BrentM](https://forums.swift.org/u/BrentM)\
**Post date:** [April 6, 2023, 7:36am UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/50 "2023-04-06T07:36:47Z")

</div>

case let statements have always been very verbose and cumbersome to work with. Especially when your associated values are also enums

I am strongly in favor of this.

---

<div class="post-metadata">

**Author:** ![ExFalsoQuodlibet](https://avatars.discourse-cdn.com/v4/letter/e/bbce88/32.png) [@ExFalsoQuodlibet](https://forums.swift.org/u/ExFalsoQuodlibet)\
**Post date:** [April 6, 2023, 7:49am UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/51 "2023-04-06T07:49:05Z")

</div>

I share @Nevin's concern about the `is case` spelling, and the fact that it conflates 2 meanings:

- matching a pattern with the `case` keyword (as is the case today, pun intended);
- asking if an enum is in a particular `case`, regardless of the associated values.

I also am not opposed to the idea of introducing a new keyword for solving an existing issue if we're going to introduce completely new syntax.

It might be productive to take a step back. One of the original design criteria of Swift was the familiarity with other `C`-like languages, while using progressive disclosure to "enhance" the meaning of existing declarations with more powerful features.

So, for example, an `enum` is defined by using the `case` keyword for each case, but the latter can also have associated values, turning it into a sum type. Also, the `switch` statement uses the `case` keyword for identifying the cases of an enum, but each `case ...:` declaration is effectively a pattern match.

We could imagine for example an alternate universe where Swift `switch` statement was spelled like this:

```swift
let value: Result<Int, Error> = .success(42)

match value {
as .success(let wrapped): print(wrapped)
as .failure: print("failure")
}

```

In addressing the need to simplify and improve code like

```swift
let value: Result<Int, Error> = .success(42)

let isSuccess = if case .success = value {
  true
} else {
  false
}

```

we should bear in mind that original criterion (that I think it still valid today).

Assuming that we want to introduce new syntax, I'd personally consider the following constraints:

- it should avoid the issue of a declaration like `if case .some = value`, which reads backwards, and it's complicated to understand and use for beginners;
- it should allow for something like `case .some = value` to be used as an expression in itself, that returns a `Bool` value;
- it should use the `case` keyword in the exact same way it's used today, that is, matching a pattern;
- it should be extendable in the future to allow binding variables, that are then returned by the expression (for example, `let wrapped: Int? = case .success(yield) = value`.

Given the constraints, I would introduce the `match` keyword, that works like `switch` but for matching a single `case`:

```swift
let value: Result<Int, Error> = .success(42)

let isSuccess = match value case .success

```

This way, `case` has exactly the same meaning, and we can avoid the `is case` spelling that makes it look like we're asking if "some expression is in a certain case" rather than "some expression matches a certain pattern".

The examples in the pitch would then read:

```swift
match foo case .bar // enum case
match foo case .bar(42) // enum case with associated values
match foo case .bar(42, _) // enum case with partially matched associated values
match foo case 42 // integer literal
match foo case true // boolean literal
match foo case "A string" // string literal
match foo case bar // other expression

```

This would extend nicely to `if` statements:

```swift
let value: Result<Int, Error> = .success(42)

if match value case .success {
  print("is success")
} else {
  print("is failure")
}

```

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 6, 2023, 11:05am UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/52 "2023-04-06T11:05:02Z")

</div>

Exploring "drop case" idea further:

```swift
enum E {
    case foo
    case bar(Int)
    case baz(Int, Int, Int)
}
var value: E = ...

value is .foo // true or false
value as? .foo // ()? or nil
value as! .foo // () or crash
value is .bar // true or false
value as? .bar // Optional(42) or nil
value as! .bar // 42 or crash
value is .baz // true or false
value as? .baz // (1, 2, 3)? or nil
value as! .baz // (1, 2, 3) or crash

```

---

<div class="post-metadata">

**Author:** ![eneko](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/eneko/32/12117_2.png) [@eneko](https://forums.swift.org/u/eneko)\
**Post date:** [April 6, 2023, 2:25pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/53 "2023-04-06T14:25:33Z")

</div>

I like how `is case` reads, could be a great addition to the language.

I would vote against allowing variable binding, to avoid developers having to decide between using `if case let` vs `if is case`.

Without variable binding, however, I cannot but think this expression is an equality expression, and wonder why not use `==` instead, like this:

```swift
foo == .bar // enum case
foo == .bar(42) // enum case with associated values
foo == .bar(42, _) // enum case with partially matched associated values
foo == 42 // integer literal
foo == true // boolean literal
foo == "A string" // string literal
foo == bar // other expression

```

There is only a mention about using `==` in the pitch but I’m not sure where the ambiguity is.

How would these two following statements be different to the compiler?

```swift
HeaderView(inThread: destination == .messageThread(_))
HeaderView(inThread: destination is case .messageThread(_))

```

If `==` was deemed not appropriate, maybe the existing match operator `~=` would be a better option, making it more usable and common for developers.

Whichever ends up being, I like this proposal.

---

<div class="post-metadata">

**Author:** ![allevato](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/allevato/32/385_2.png) [@allevato](https://forums.swift.org/u/allevato)\
**Post date:** [April 6, 2023, 2:33pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/54 "2023-04-06T14:33:34Z")

</div>

> [@ExFalsoQuodlibet](#):
>
> I share @Nevin's concern about the `is case` spelling, and the fact that it conflates 2 meanings:
> 
> - matching a pattern with the `case` keyword (as is the case today, pun intended);
> - asking if an enum is in a particular `case`, regardless of the associated values.

These are actually the same meaning; specifically, the latter is a subset of the former.

I wonder if the reason people are getting hung up here is because they associate `case` with enum cases more strongly, due to history in other languages. But if we focus on Swift holistically, the `case` in `if case` is really analogous to the `case` in a **`switch`** statement, not to the `case`s in an `enum`. The latter are just value constructors, and the relationship to the other `case` is that they happen to let you involve their discriminator in that matching operation—that's why the syntax to match them is `.name`, not just `name` as they're declared in the `enum`. So it's that usage of the keyword that's the odd one out, not the one used for matching.

In my opinion, the awkwardness around `if case` historically in the language isn't around the `if case <pattern>` part, because that's written exactly as you would write it in a `switch` statement, but rather it's around the _reversal of the ordering_ between the pattern and the value being matched. Since you have to write `if case <pattern> = <value>`, it reads in the opposite way that you'd expect, and it also causes problems for tooling like auto-completion because, if you type the pattern first, the IDE doesn't have the context to infer what the valid patterns are. `is case` doesn't suffer from this problem.

Those issues aren't the fault of the `case` keyword specifically. I wonder if a shift in thinking toward "`case` as in `switch`, not `case` as in `enum`" would help resolve many of the concerns here. When I think of it in those terms, `is case` fits perfectly with the existing usage in the language and I don't think a new keyword that would make a second way to do the same thing is warranted.

---

<div class="post-metadata">

**Author:** ![Zollerboy1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/zollerboy1/32/12997_2.png) [@Zollerboy1](https://forums.swift.org/u/Zollerboy1)\
**Post date:** [April 6, 2023, 3:44pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/55 "2023-04-06T15:44:13Z")

</div>

> [@eneko](#):
>
> There is only a mention about using `==` in the pitch but I’m not sure where the ambiguity is.

`==` is a function that takes two values of the same type and compares them. But what we want here, is to match a value to a pattern, which is not required to be a complete value. E.g. in your example `foo == .bar(42, _)`, we have the pattern `.bar(42, _)` which we cannot pass to `==` in any way since it's impossible to represent it as a value of the same type as `foo` is. So the big difference between `==` and the proposed `is case` is that the former matches two values against each other while the latter matches a value against a pattern which is a fundamentally different thing.

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [April 6, 2023, 6:22pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/56 "2023-04-06T18:22:01Z")

</div>

On top of that `==` requires `Equatable`, so even when it "works" (`value == .foo`) it's not what we want, as the type is not necessarily `Equatable`.

* * *

I believe everyone here is in agreement that variable should be on the left and discriminator on the right, essentially we are looking for XXX and/or YYY:

```
value XXX .baz // false or true

```

potentially also:

```
value XXX .baz(1, 2, 3) // false or true, do we need this?
value XXX .baz(1, 2, _) // false or true, do we need this?
value YYY .baz // (1, 2, 3) or nil, do we need this?

```

and discussing what XXX should be, and also whether we want all flavours of XXX or not/or YYY as well.

The variants discussed so far for XXX:

1. `is case`
2. `is`
3. `case`
4. `matches`
5. ~=
6. nothing (use YYY instead, e.g. `value.baz != nil`)

and for YYY:

1. `as? case`, `as! case`
2. `as?`, `as!`
3. `<nothing>` (e.g `value.baz`).

---

<div class="post-metadata">

**Author:** ![GreatApe](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/greatape/32/2640_2.png) [@GreatApe](https://forums.swift.org/u/GreatApe)\
**Post date:** [April 6, 2023, 6:56pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/57 "2023-04-06T18:56:34Z")

</div>

Everyone is probably in agreement, except me. I still think it would be nicer to have an operator that converts the unknown enum value to its discriminant, then that result can be used with ==, !=, put in an array or whatever. Seems more flexible and modular.

---

<div class="post-metadata">

**Author:** ![Zollerboy1](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/zollerboy1/32/12997_2.png) [@Zollerboy1](https://forums.swift.org/u/Zollerboy1)\
**Post date:** [April 6, 2023, 8:45pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/58 "2023-04-06T20:45:14Z")

</div>

AFAICS, your approach wouldn't allow doing something like `foo is case .bar(45, _)`. So how is it more flexible?

---

<div class="post-metadata">

**Author:** ![GreatApe](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/greatape/32/2640_2.png) [@GreatApe](https://forums.swift.org/u/GreatApe)\
**Post date:** [April 6, 2023, 9:37pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/59 "2023-04-06T21:37:11Z")

</div>

It would be more flexible in the ways I mentioned above. But you are right that it probably wouldn't support partial matches.

---

<div class="post-metadata">

**Author:** ![michelf](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/michelf/32/1180_2.png) [@michelf](https://forums.swift.org/u/michelf)\
**Post date:** [April 6, 2023, 10:01pm UTC](https://forums.swift.org/t/pitch-is-case-expressions/64185/60 "2023-04-06T22:01:43Z")

</div>

I am sort-of in favor of this proposal. But personally I wonder if this shouldn't be pushed a bit further.

The way I see it, a pattern without bindings is pretty much equivalent to a function (or closure) that takes some input and returns a boolean: a predicate. So we just need to allow `case .foo(1.0, _)` to convert to a type and then provide operators to use that type.

For instance, if we had this:

```swift
enum Foo { case a(Int, Int), b(Float, Float) }
let foo = Foo.a
let isMatching = foo ~= case .a(1, _)

```

This last line could be desugared like this:

```swift
let isMatching = foo ~= Pattern {
   switch $0 {
   case .a(1, _): return true
   default: return false
   }
}

```

Here, a `Pattern` type encapsulates a pattern matching closure, allowing the `~=` operator to work and return a boolean telling you if there's a match. And since it's a type you can pass the pattern around pretty easily:

```swift
let pattern = case Foo.a(1, _)
let isMatching = foo ~= pattern

```

And so you could pass it to functions, allowing things like this (with the appropriate `filter` overload):

```swift
let foos = [Foo.a(1, 2), Foo.b(3, 4), Foo.a(5, 6)]
let matchingFoos = foos.filter(case .a(1, _))

```

This direction seems much more interesting that the current proposal... except perhaps for the part where introducing a type would make back-deployment difficult.

[Previous page](https://forums.swift.org/t/pitch-is-case-expressions/64185.md?page=2)

[Next page](https://forums.swift.org/t/pitch-is-case-expressions/64185.md?page=4)
