pointfreeco / swift-case-paths

🧰 Case paths extends the key path hierarchy to enum cases.
https://www.pointfree.co/collections/enums-and-structs/case-paths
MIT License
904 stars 105 forks source link

Fixes around optionals and partial case key paths #147

Closed stephencelis closed 7 months ago

stephencelis commented 7 months ago

We recently added code that can flatten optionals in a couple of ways, but because these methods weren't disfavored, they take precedence and can produce case key paths incapable of expressing what you want, like the ability to embed an optional in an case that contains one:

@CasePathable
enum Foo {
  case bar(Int?)
}

let kp = \Foo.Cases.bar  // CaseKeyPath<Foo, Int>, not <Foo, Int?>
kp(nil)  // 'nil' is not compatible with expected argument type 'Int'

This PR disfavors these overloads to allow you to flatten optionals contextually, but by default will leave them alone.

It also fixes a bug in PartialCaseKeyPath.callAsFunction that would prevent non-optionals from being promoted to optionals in cases that expect them:

let kp: PartialCaseKeyPath<Foo> = \.bar

// Before:
kp(42)  // nil

// After:
kp(42)  // Foo.bar(.some(42))

Finally, this PR fixes a bug in which PartialCaseKeyPath.callAsFunction was technically available on CaseKeyPath, which meant you could call it with any value:

let kp = \Foo.Cases.bar

// Before:
kp("Hello")  // nil

// After:
kp("Hello")  // Cannot convert value of type 'String' to expected argument type 'Int?'