[swift-evolution] String Comparison (was: Strings in Swift 4)

Jonathan Hull jhull at gbis.com
Tue Jan 24 03:06:22 CST 2017


Breaking this into a different thread…

One suggestion which I haven’t seen (maybe it is too crazy) is the idea of introducing shadowing of operators within a scope (similar to variables).

Now this would need a TON of design work to make it actually work, but the gist would be:

	if blah == foo {
		//We can override/shadow the ‘<‘ operator within this scope as long as the signature is the same
		let (<) = String.comparisonOperation(case: .insensitive, locale: .current) //Here we grab a closure produced by a static function on String
		
		if a < b {	//Within this scope < will be case insensitive
			//Stuff
		}
	}
	//Outside of the scope, < is back to it’s lovable self


Thoughts on the general idea?

Thanks,
Jon


> I know you want to defer this for now, so feel free to set this part of the email aside, but here's a quick list of solutions I've ballparked:
> 
> 1. Your "one operand carries the options" solution.
> 
> 2. As I mentioned, do something that effectively overloads comparison operators to return them in a symbolic form. You're right about the ambiguity problem, though.
> 
> 3. Like #2, but with slightly modified operators, e.g.:
> 
> 	if localized(fu &< br, case: .insensitive) { … }
> 
> 4. Reintroduce something like the old `BooleanType` and have *all* comparisons construct a symbolic form that can be coerced to boolean. This is crazy, but actually probably useful in other places; I once experimented with constructing NSPredicates like this.
> 
> 	protocol BooleanProtocol { var boolValue: Bool { get } }
> 	
> 	struct Comparison<Operand: Comparable> {
> 		var negated: Bool
> 		var sortOrder: SortOrder
> 		var left: Operand
> 		var right: Operand
> 		
> 		func evaluate(_ actualSortOrder: SortOrder) -> Bool {
> 			// There's circularity problems here, because `==` would itself return a `Comparison`, 
> 			// but I think you get the idea.
> 			return (actualSortOrder == sortOrder) != negated
> 		}
> 	}
> 	extension Comparison: BooleanProtocol {
> 		var boolValue: Bool {
> 			return evaluate(left.compared(to: right))
> 		}
> 	}
> 	
> 	func < <ComparableType: Comparable>(lhs: ComparableType, rhs: ComparableType) -> Comparison {
> 		return Comparison(negated: false, sortOrder: .before, left: lhs, right: rhs)
> 	}
> 	func <= <ComparableType: Comparable>(lhs: ComparableType, rhs: ComparableType) -> Comparison {
> 		return Comparison(negated: true, sortOrder: .after, left: lhs, right: rhs)
> 	}
> 	// etc.
> 	
> 	// Now for our special String comparison thing:
> 	func localized(_ expr: Comparison<String>, case: StringCaseSensitivity? = nil, …) -> Bool {
> 		return expr.evaluate(expr.left.compare(expr.right, case: case, …))
> 	}
> 
> 5. Actually add some all-new piece of syntax that allows you to add options to an operator. Bad part is that this is ugly and kind of weird; good part is that this could probably be used in other places as well. Strawman example:
> 
> 	// Use:
> 	if fu < br %(case: .insensitive, locale: .current) { … }
> 	
> 	// Definition:
> 	func < (lhs: String, rhs: String, case: StringCaseSensitivity? = nil, …) -> Bool { … }
> 
> 6. Punt on this until we have macros. Once we do, have the function be a macro which alters the comparisons passed to it. Bad part is that this doesn't give us a solution for at least a version or two.


More information about the swift-evolution mailing list