[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