Swift switch patterns matching with .some/.none - swift

var stream: DataStream? = nil
switch stream {
case nil:
print("No data stream is configured.")
case let x?:
print("The data stream has \(x.availableBytes) bytes available.")
}
Refrenced by switch optional code, the above content will call public func ~= <T>(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool function. And there is another switch pattern in this function:
public func ~= <T>(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool {
switch rhs {
case .some(_):
return false
case .none:
return true
}
}
My question is whitch function will be called by this pattern? There are two ~= overload functions in Range.swift & Policy.swift, does one of them will be called?

Your question is not totally clear to be but ~= is the pattern match operator.
func ~=(pattern: ???, value: ???) -> Bool
By overriding it you are able to create your own pattern match for your custom types and use it into a switch (basically the switch call this operator).
There is a good article here https://appventure.me/2015/08/20/swift-pattern-matching-in-detail/
What are you seeing in Range and Policy are simply the custom overload of this operator to make it work with Range and Policy types as they did for optionals.

Related

Enum to map to a comparable key path

I'm writing some code where a user can select how a particular array of data is sorted. I was trying to see if I could hold the set of permissible sort properties in an enum. What I want to be able to express is something like:
import Foundation
struct MyStruct {
let a: Int
let b: Int
}
enum MyStructProps {
case a, b
func comparableKeyPath<T: Comparable>() -> KeyPath<MyStruct, T> {
switch self {
case .a: return \MyStruct.a
case .b: return \MyStruct.b
}
}
}
At the moment each case returns a compiler error: key path value type 'Int' cannot be converted to contextual type 'T'.
Looking at the post Swift Generics, Constraints, and KeyPaths I would need to embed this within a sort function, so that Swift knows how to derive the type of the generic key path.
But I was curious to learn if there is a way of returning a generic keypath in my naive code?
If you need to work at some more intermediate level than the following, you'll need to type-erase, as Sweeper says in the comment.
Otherwise, because you can't return different types from one function, just employ generics for the intermediate steps, and have one function at the end of the process that employs multiple types.
extension Sequence where Element == MyStruct {
func sorted(by property: Element.ComparableProperty) -> [Element] {
switch property {
case .a: return sorted(by: \.a)
case .b: return sorted(by: \.b)
}
}
}
extension MyStruct {
enum ComparableProperty {
case a, b
}
}
public extension Sequence {
/// Sorted by a common `Comparable` value.
func sorted<Comparable: Swift.Comparable>(
by comparable: (Element) throws -> Comparable
) rethrows -> [Element] {
try sorted(by: comparable, <)
}
/// Sorted by a common `Comparable` value, and sorting closure.
func sorted<Comparable: Swift.Comparable>(
by comparable: (Element) throws -> Comparable,
_ areInIncreasingOrder: (Comparable, Comparable) throws -> Bool
) rethrows -> [Element] {
try sorted {
try areInIncreasingOrder(comparable($0), comparable($1))
}
}
}

Custom infix operators and optionals

class TreeNode: Equatable {
static func ==(lhs: TreeNode, rhs: TreeNode) -> Bool {
lhs.val == rhs.val && lhs.left == rhs.right && lhs.right == rhs.left
}
var val: Int = 0
var left, right: TreeNode?
}
This code compiles and even works. But why? left and right variables are optional, isn't I supposed to unwrap it first in the body of static func ==?
Actually it isn't quite an equation. As you can see it's rather some sort of symmetrical equation. So I would like to define custom operator with different name for this purpose:
infix operator =|=: ComparisonPrecedence
class TreeNode {
static func =|=(lhs: TreeNode, rhs: TreeNode) -> Bool {
lhs.val == rhs.val && lhs.left =|= rhs.right && lhs.right =|= rhs.left
}
var val: Int = 0
var left, right: TreeNode?
}
And now it doesn't compile due to the reason I've mentioned earlier. It wants me to unwrap the optionals first.
Actually it would be great if it "just works" like in the case of "=="))) Because not having to unwrap the optionals explicitly would be convenient here.
So I want to understand why it behaves differently in these two situations.
This code compiles and even works. But why?
It is simply because there is an == operator declared for all Optional<Wrapped> where Wrapped is Equatable, like this:
static func == (lhs: Wrapped?, rhs: Wrapped?) -> Bool
TreeNode is Equatable in your first code snippet, so it works.
In your second code snippet, you haven't declared a =|= operator that operates on two TreeNode?. You can do that by either putting this in global scope...
func =|= (lhs: TreeNode?, rhs: TreeNode?) -> Bool {
switch (lhs, rhs) {
case (nil, nil): // both nil
return true
case (let x?, let y?): // both non-nil
return x =|= y // compare two non-optional tree nodes
default:
return false
}
}
or writing an Optional extension:
extension Optional where Wrapped == TreeNode {
static func =|= (lhs: Wrapped?, rhs: Wrapped?) -> Bool {
switch (lhs, rhs) {
case (nil, nil): // both nil
return true
case (let x?, let y?): // both non-nil
return x =|= y // compare two non-optional tree nodes
default:
return false
}
}
}
But as Leo Dabus said, I'd just conform to Equatable and not create your own operator. Conforming to existing protocols allows you to use TreeNode with many APIs in the standard library, such as Array.contains(_:).

Swift - Reference default comparison function as a function parameter

I'm trying to implement a convenience Collection.sorted(by: KeyPath) function.
So far, it works if do
func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] {
return sorted { lhs, rhs
return lhs[keyPath: keyPath] < rhs[keyPath: keyPath]
}
}
But what if I want to allow the caller to specify the actual sorting logic ? I added a callback to perform the comparison, like such (taking inspiration from the orginal sorted(_:) function signature).
func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>, _ compare: (T, T) throws -> Bool) rethrows -> [Element] {
return try sorted { lhs, rhs in
return try compare(lhs[keyPath: keyPath], rhs[keyPath: keyPath])
}
}
Now, this is all works, but it means the callsite always has to specify which sorting operation to perform.
let sorted = myArray.sorted(by: \.name, <)
I'd like it to default to <, but how can I reference the < operator by default, in my function's signature ?
It is actually possible to reference the un-applied < function by wrapping it in parentheses (<) when using it as a default parameter.
func sorted<T: Comparable>(
by keyPath: KeyPath<Element, T>,
_ compare: (T, T) throws -> Bool = (<)
) rethrows -> [Element] {
return try sorted { lhs, rhs in
return try compare(lhs[keyPath: keyPath], rhs[keyPath: keyPath])
}
}
However, there is currently an issue with the compiler when doing this.
Even though < doesn't throw, the compiler will still enforce you to use try at the call site.
A bug report for this was opened quite some time ago, and is still unresolved. If you run into this, please upvote it: https://bugs.swift.org/browse/SR-1534
Additionally, as pointed out in the comments, the sorted(by:) function is actually 2 different functions.
One requires Comparable and uses < internally, while the other lets you specify the sorting logic directly and thus, does not require Comparable conformance.
Therefore, this convenience sorting by keyPath would still require 2 functions.

Equatable with enum - Swift

How can I validate equal with below source code?
enum ErrorAPI: Equatable, Error {
case CannotFetch(String)
}
func ==(lhs: ErrorAPI, rhs: ErrorAPI) -> Bool {
switch (lhs, rhs) {
case (.CannotFetch(let a), .CannotFetch(let b)) where a == b:
return true
default:
return false
}
}
The enum case CannotFetch has an associated value of type String. That means, when the enum case is set to CannotFetch, a specific String is associated with that case. Read through the official Swift documentation about Enumerations if you need to understand Associated Values.
The func ==() method is a so called Equivalence Operator. You can read more about it in the official Swift documentation about Advanced Operators.
When comparing two enums, lets call them enumOne and enumTwo, this function is implemented to be able to compare those two enums if both cases are CannotFetch(String).
Example:
let enumOne = ErrorAPI.CannotFetch("Hi")
let enumTwo = ErrorAPI.CannotFetch("Hello")
if enumOne != enumTwo {
print("They are not equal!")
} else {
print("They are equal")
}
The line case (.CannotFetch(let a), .CannotFetch(let b)) where a == b: works as follows:
This case is only proceeded if both, enumOne and enumTwo, are set to the case CannotFetch(String)
Take the associated value (=String) of the left hand sided enum, i.e. "Hi" and assign it to the new constant let a.
Take the associated value (=String) of the left hand sided enum, i.e. "Hello" and assign it to the new constant let b.
Additionally, Check if the values behind the constants a and b are equal, i.e. Check if the Strings Hi and Hello are equal.
If all this is true, execute the code block in the case, i.e. return true

Overriding / operator for Optionals using generics results in endless loop

lets take a look at the following code snippet:
func / <T>(lhs: T?,rhs: T?) throws -> T? {
switch (lhs,rhs) {
case let (l?,r?):
return try l/r
default:
return nil
}
}
let x : Double? = 2
let y : Double? = 2
let z = try! x/y
I created a generic function that expects two optional parameters. If I run this code it leads to an endless loop because try l/r uses func / <T>(lhs: T?,rhs: T?) to divide the values. Can anyone explain why dividing two none optional double values results in a function call to the method I wrote and not the default / operator definition for Double?
If I extend Double by an extension that requires a static / operator for that class everything works like a charm:
protocol Dividable {
static func /(lhs: Self, rhs: Self) -> Self
}
extension Double: Dividable {}
func / <T:Dividable>(lhs: T?,rhs: T?) throws -> T? {
switch (lhs,rhs) {
case let (l?,r?):
return l/r
default:
return nil
}
}
let x : Double? = 2
let y : Double? = 2
let z = try! x/y
The binary arithmetic for e.g. Double is not implemented using concrete Double types, but rather as default generic implementations for types conforming to FloatingPoint:
swift/stdlib/public/core/FloatingPoint.swift.gyb
Within the block of your custom / function, the compiler does not know that the typeholder T conforms to FloatingPoint, and the overload resolution of l/r will resolve to the method itself (since the FloatingPoint implementions, while being more specific, are not accessible to the more general non-constrained type T in your custom implementation).
You could workaround this by adding FloatingPoint as a type constraint also to your own custom method:
func /<T: FloatingPoint>(lhs: T?, rhs: T?) throws -> T? {
switch (lhs, rhs) {
case let (l?, r?):
return try l/r
default:
return nil
}
}
Likewise, the binary arithmetic for integer types are implemented as default generic implementations constrained to types conforming to the internal protocol _IntegerArithmetic, to which the public protocol IntegerArithmetic conforms.
swift/stdlib/public/core/IntegerArithmetic.swift.gyb
You can use the latter public protocol to implement an overload of your custom operator function for integer types.
func /<T: IntegerArithmetic>(lhs: T?, rhs: T?) throws -> T? {
switch (lhs, rhs) {
case let (l?, r?):
return try l/r
default:
return nil
}
}
Finally, you might want to consider why you'd want this function to throw. N also ote that there are ways to simplify you implementations when dealing with exactly two optional values that you want to operate on only in case both differ from nil. E.g.:
func /<T: FloatingPoint>(lhs: T?, rhs: T?) -> T? {
return lhs.flatMap { l in rhs.map{ l / $0 } }
}
func /<T: IntegerArithmetic>(lhs: T?, rhs: T?) -> T? {
return lhs.flatMap { l in rhs.map{ l / $0 } }
}
Of, if you prefer semantics over brevity, wrap your switch statement in a single if statement
func /<T: FloatingPoint>(lhs: T?, rhs: T?) -> T? {
if case let (l?, r?) = (lhs, rhs) {
return l/r
}
return nil
}
func /<T: IntegerArithmetic>(lhs: T?, rhs: T?) -> T? {
if case let (l?, r?) = (lhs, rhs) {
return l/r
}
return nil
}
Your function signature doesn't let the compiler know anything about the type of lhs and rhs, other than that they're the same type. For example you could call your method like this:
let str1 = "Left string"
let str2 = "Right string"
let result = try? str1 / str2
This will result in an infinite loop because the only method that the compiler knows called / that takes in 2 parameters of the same type (in this case String) is the one that you've declared; return try l/r will call your func / <T>(lhs: T?,rhs: T?) throws -> T? method over and over again.
As you mentioned in your question, you will need a protocol that your parameters must conform to. Unfortunately there is no existing Number or Dividable protocol that would fit your needs, so you'll have to make your own.
Note that division will crash when the denominator is 0 and will not throw an error, so you should be able to remove the throws keyword from your function so that it is:
func / <T:Dividable>(lhs: T?, rhs: T?) -> T?
Edit to clarify further
If you think about what the compiler knows at that point I think it makes more sense. Once inside the function all the compiler knows is that lhs and rhs are of type T and optional. It doesn't know what T is, or any of its properties or functions, but only that they're both of type T. Once you unwrap the values you still only know that both are of type T and non-optional. Even though you know that T (in this instance) is a Double, it could be a String (as per my example above). This would require the compiler to iterate over every possible class and struct to find something that supports your method signature (in this case func / (lhs: Double, rhs: Double) -> Double), which it simply can't do (in a reasonable time), and would lead to unpredictable code. Imagine if you added this global method and then every time / was used on something existing (such as Float(10) / Float(5)) your method was called, that would get pretty messy and confusing pretty quickly.