recently I've programmed a lot of Javascript. And in JS the expressions I wanted to be evaluated were something like:
expirationDate < new Date();
Which is expirationDate is undefined returns false (which is the intended behaviour, as nil/null/undefined is neither earlier nor equal, nor later than any Date object.
I now had the same issue in Swift, I am maintaining an optional expirationDate which is compared to the current date at some positions in my code.
Unfortunately Comparable (which Date conforms to) expects non optional parameters.
So my choice was to always perform:
if let date = expirationDate {
// compare date to Date()
} else {
// handle the 'false' case
}
expirationDate < Date() would throw an error.
So I started creating my own operator for that scenario. (I at first used an extension on Date and overloaded the < operator with optional parameters):
infix operator <?: ComparisonPrecedence
public func <? <T>(lhs: T?, rhs: T) -> Bool where T: Comparable {
if let leftSide = lhs {
return leftSide < rhs
}
return false
}
I also added those functions for <=, >=, >, == etc. In the example of Date it makes sense as you can't tell what nil is compared to Date().
I'd argue the same for numbers or any comparison for that matter.
Do you know any example in which someOptional < someComparable should evaluate true by default if someOptional is in fact nil? If you need to default to true, you can still use the if let construct.
Any examples or counter-examples on why this is not helpful are greatly appreciated. Thanks!
The nuance I see is that if the return type from your operator is false, you don't know if it's because the comparison failed (lhs >= rhs), or if the lhs is .none. Maybe return an optional Bool should be considered ( a kind-of "tri-state" Bool) if one of the operands is T? and the operand is .none?
Another pattern I use in these cases is to provide a failing (or succeeding) default for the comparison, depending on the behavior I want. If nil should mean the subscription (or whatever) is still valid, you can write:
let isStillActive = (expirationDate ?? .distantFuture) > Date()