# Why is my custom \`~=\` operator not detected?

**URL:** <https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431>\
**Category:** Using Swift\
**Tags:** pattern-matching\
**Created:** [September 7, 2024, 4:24pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431 "2024-09-07T16:24:14Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![bbrk24](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bbrk24/32/27370_2.png) [@bbrk24](https://forums.swift.org/u/bbrk24)\
**Post date:** [September 7, 2024, 4:24pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431/1 "2024-09-07T16:24:14Z")

</div>

I'm trying to demonstrate to someone that you can use a custom operator `~=` for pattern-matching. Yet, for some reason, when I try to run the code below, I always get an error "pattern of type 'Parity' cannot match 'Int'":

```swift
enum Parity {
  case even, odd
  
  static func ~= (lhs: Parity, rhs: Int) -> Bool {
    return (lhs == .even) == rhs.isMultiple(of: 2)
  }
}

let str = (
  switch 2 {
  case Parity.even: "even"
  case Parity.odd: "odd"
  default: fatalError("No exhaustivity checking :(")
  }
)

print(str)

```

It doesn't matter if I put the `func ~=` as a `static` member of `Parity`, as a `static` member in an `extension Int`, or as a global function. It just doesn't work. I also tried switching the operands around in case I have those backwards, but that wasn't it either. What is going on here?

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [September 7, 2024, 4:42pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431/2 "2024-09-07T16:42:21Z")

</div>

I think what's happening here is that enum cases are treated specially in pattern matching rather than 'just' being values. This is in some sense necessary to be able to do things like match a case with an associated value using only the case name, without materializing a full value of the enum (or match against enums which don't conform to `Equatable`). The pattern matching operator is only used in the case of expression patterns, but since enum cases form enum patterns we never get around to using the expression matching.

Your example compiles for me if I switch to a `struct`:

```swift
struct Parity: Equatable {
  private var isEven: Bool
  static let even = Parity(isEven: true)
  static let odd = Parity(isEven: false)

  static func ~= (lhs: Parity, rhs: Int) -> Bool {
    return (lhs == .even) == rhs.isMultiple(of: 2)
  }
}

let str = switch 2 {
  case Parity.even: "even"
  case Parity.odd: "odd"
  default: fatalError("No exhaustivity checking :(")
}

print(str)

```

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [September 7, 2024, 8:19pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431/3 "2024-09-07T20:19:49Z")

</div>

Some workarounds for this [longstanding issue](https://github.com/swiftlang/swift/issues/43734) (all of them are "make this not look like a simple member access to an enum case"):

- Add `.self` to each case (`case Parity.even.self`)
- Wrap in an identity function call (`case id(Parity.even)`, if you've defined `func id`)
- Wrap in a closure (`case {Parity.even}()`)
- Delete the type qualification; weirdly, using `case .even` is sufficient to force a context-sensitive search (but only because `Int` doesn't have an `even` static member)

---

<div class="post-metadata">

**Author:** ![bbrk24](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/bbrk24/32/27370_2.png) [@bbrk24](https://forums.swift.org/u/bbrk24)\
**Post date:** [September 8, 2024, 7:08pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431/5 "2024-09-08T19:08:56Z")

</div>

> [@jrose](#):
>
> weirdly, using `case .even` is sufficient to force a context-sensitive search

Huh, I figured that wouldn't work precisely because `Int.even` doesn't exist. Thanks for the suggestions.

---

<div class="post-metadata">

**Author:** ![Jumhyn](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jumhyn/32/3891_2.png) [@Jumhyn](https://forums.swift.org/u/Jumhyn)\
**Post date:** [September 8, 2024, 7:42pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431/6 "2024-09-08T19:42:20Z")

</div>

> [@bbrk24](#):
>
> Huh, I figured that wouldn't work precisely because `Int.even` doesn't exist.

Once we hit the fallback to expression pattern matching, the compiler looks up _all_ definitions for `~=` for the given matchee. This finds the generic `~=<T: Equatable>` overload, which obviously doesn't work since there's no `Int.even` as you mention, but the custom `(Parity, Int)` overload is notionally calling `.even ~= 2`, so the implicit member lookup happens with `Parity` type context, not `Int` type context!

Dropping the type context also didn't occur to me but it's a clever way to sidestep the 'enum case' special treatment because that logic relies on being able up-front identify "this is a reference to an enum case". By delaying that until overload resolution time we can ensure that the pattern is resolved as an expression pattern rather than an enum case pattern!

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [September 8, 2024, 8:14pm UTC](https://forums.swift.org/t/why-is-my-custom-operator-not-detected/74431/7 "2024-09-08T20:14:19Z")

</div>

To be honest I didn’t think it would work either, I just tried it on a whim!
