[swift-evolution] [swift-evolution-announce] [Review] SE-0067: Enhanced Floating Point Protocols
Tony Allevato
allevato at google.com
Tue Apr 26 21:34:21 CDT 2016
On 2016-04-26 22:32:16 +0000, Dave Abrahams via swift-evolution said:
> The main reasons to route through a single generic operator
> implementation are:
>
> * User experience; we want to cut down the number of overloads of any
> operator to a manageable set, in part because they live in the global
> namespace. When you look at a list of functions in the global
> namespace, seeing fifty instances of `func +` is not helpful.
>
> * Type checker speed. Having all of these overloads around has
> historically put a strain on the type checker and made compilation
> slow. That may be less true today than it once was, though.
These are both completely understandable concerns (especially the type
checker speed, since I've hit the "expression too complex" problem in a
couple odd places myself). I'm hoping though that we can converge on a
solution other than "pollute the public interface of a type with named
methods that duplicate the operators, which are trampolines to those
methods", since I think that actually detracts from the user experience
of using that type.
Would something like this be possible? Imagine protocols defined like this:
public protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Self
}
public protocol FloatingPoint: Equatable {
static func + (lhs: Self, rhs: Self) -> Self
}
This would have the effect of automatically generating the following
generic trampoline global operators:
public func == <T: Equatable>(lhs: T, rhs: T) -> T {
return T.==(lhs, rhs)
}
public func + <T: FloatingPoint>(lhs: T, rhs: T) -> T {
return T.+(lhs, rhs)
}
Then, types that conform to FloatingPoint would define the method as
appropriate:
public struct Double: FloatingPoint {
public static func == (lhs: Double, rhs: Double) -> Double { ... }
public static func + (lhs: Double, rhs: Double) -> Double { ... }
}
If something like this worked, it has a number of advantages:
* It has the same lower overhead with respect to the global namespace
as the currently proposed strategy that uses an explicitly defined
generic operator that trampolines to a named add() method. This should
keep type checking fast and keeps the number of global instances of the
operator low.
* It increases consistency: the operator requirement is declared in the
protocol, and the concrete implementation is *in* the conforming type
(as opposed to a separate global function).
* There is no need to introduce arbitrarily named methods to implement
the operations that would pollute the public interface of the type.
* It doesn't affect the ability to pass operators as first-class
functions into other algorithms (like + into reduce). In fact, if an
algorithm needs to be explicit about the types (if they can't be
inferred for some reason), then for example `Float.+` would refer to
the appropriate `(Float, Float) -> Float` function.
Some might argue that implicitly defining the generic global operator
under the covers is a drawback to this approach, but I think that's a
minor concern when weighed against the alternatives.
Individual elements of the syntax could be debated (an "operator"
keyword instead of "static func"? But that might just be syntactic
sugar), but overall, I'd love to get feedback on the feasibility of an
approach like this. I'm happy to take a stab at drawing this up in a
proposal, as well.
More information about the swift-evolution
mailing list