What is the ~> (tilde greater than) operator used for in Swift?

前端 未结 3 1031
一个人的身影
一个人的身影 2020-12-03 17:37

Swift 1.1 includes the declaration of the ~> operator:

infix operator ~> {
    associativity left
    precedence 255
}

What is this used

相关标签:
3条回答
  • 2020-12-03 18:11

    Since Swift has been open-sourced we can see the actual reason for including the ~> in stdlib: as a workaround for method specialization in a child protocol in Swift 1.x.

    // Workaround for <rdar://problem/14011860> SubTLF: Default
    // implementations in protocols.  Library authors should ensure
    // that this operator never needs to be seen by end-users.  See
    // test/Prototypes/GenericDispatch.swift for a fully documented
    // example of how this operator is used, and how its use can be hidden
    // from users.
    infix operator ~> { associativity left precedence 255 }
    

    A detailed example can be found in test/Prototypes/GenericDispatch.swift.

    Note: Do not use ~> in Swift 2+. This is a historical workaround. It is no longer needed. Read on.


    How ~> works

    In Swift 2, the only remaining instance of ~> is the abs function (which is probably going away as well). We can see how actually ~> works. From stdlib/public/core/IntegerArithmetic.swift.gyb, the SignedInteger protocol defines an ~> operator:

    struct _Abs {}
    
    protocol SignedNumber: Comparable {
        prefix func -(_: Self) -> Self
    
        func ~> (_: Self, _: (_Abs, ()) -> Self
    }
    
    func ~> <T: SignedNumber>(x: T, _: (_Abs, ()) -> T {
        return x < 0 ? -x : x
    }
    

    Here, the RHS of the arrow ~> specifies what command can be sent to the object. Think of p~>(cmd, args) as something like p.cmd(args).

    Then we provide the default implementation of _Abs when given a SignedNumber.

    protocol AbsoluteValuable : SignedNumber {
        static func abs(_: Self) -> Self
    }
    
    func ~> <T: AbsoluteValuable>(x: T, _: (_Abs, ())) -> T {
        return T.abs(x)
    }
    

    Next, we have a child protocol, AbsoluteValuable, which perhaps has a more efficient way to compute the absolute value than x < 0 ? -x : x. We then specialize the ~> operator for AbsoluteValuable.

    func abs<T: SignedNumber>(_ x: T) -> T {
        return x ~> (_Abs(), ())
    }
    

    Finally we hide the ~> call in a public wrapper method. If T is actually an AbsoluteValuable, the more specialized and thus more efficient ~> will be chosen.

    This is also why we get 42 ~> _advance(12) or 42 ~> _distanceTo(23) as shown in @rintaro's answer, because the .advanceBy and .distanceTo methods are O(n) for a general ForwardIndexType, but can be implemented in O(1) if the type is RandomAccessIndexType.


    You don't need ~>

    This pattern could also be done without invoking the ~> operator, using extensions on a protocol:

    protocol SignedInteger: Comparable {
        prefix func -(_: Self) -> Self
    
        func genericAbs() -> Self
    }
    
    extension SignedInteger {
        func genericAbs() -> Self {
            return self < 0 ? -self : self
        }
    }
    
    protocol AbsoluteValueable: SignedInteger {
        static func abs(_: Self) -> Self
    }
    
    extension AbsoluteValueable {
        func genericAbs() -> Self {
            return Self.abs(self)
        }
        // normally you would allow subtypes to override
        // genericAbs() directly, instead of overriding 
        // static abs().
    }
    
    func abs<T: SignedInteger>(x: T) -> T {
        return x.genericAbs()
    }
    

    In particular this is why all other ~> implementations besides abs are gone in Swift 2: all specializations using this technique has been changed to use protocol extensions which is more obvious, e.g.

    • underestimateCount in commit c48d6aa0 (2015 Apr 16)
    • advancedBy, distanceTo in commit 311baf73 (2015 Aug 4)
    • etc.

    Note: The radar problem 14011860 is not public, but we may see the scope of the bug by its duplicates on OpenRadar:

    • #17298843 "Swift: Support protocol default methods and protocol extensions (=mixins)"
    • #17412469 "Swift - Allow adding functions with implementation to protocols"
    0 讨论(0)
  • 2020-12-03 18:17

    On SwiftDoc this operator has been removed from the operators chapter for Swift 3.0.

    0 讨论(0)
  • 2020-12-03 18:24

    It seems, It's related collection/sequence/index types

    According to Defines-Swift:

    protocol SequenceType : _Sequence_Type {
      typealias Generator : GeneratorType
      func generate() -> Generator
      func ~>(_: Self, _: (_UnderestimateCount, ())) -> Int
      func ~><R>(_: Self, _: (_PreprocessingPass, ((Self) -> R))) -> R?
      func ~>(_: Self, _: (_CopyToNativeArrayBuffer, ())) -> _ContiguousArrayBuffer<Self.Generator.Element>
    }
    
    protocol CollectionType : _CollectionType, SequenceType {
      subscript (position: Self.Index) -> Self.Generator.Element { get }
      func ~>(_: Self, _: (_CountElements, ())) -> Self.Index.Distance
    }
    
    protocol ForwardIndexType : _ForwardIndexType {
      func ~>(start: Self, _: (_Distance, Self)) -> Self.Distance
      func ~>(start: Self, _: (_Advance, Self.Distance)) -> Self
      func ~>(start: Self, _: (_Advance, (Self.Distance, Self))) -> Self
    }
    
    protocol SignedNumberType : _SignedNumberType {
      prefix func -(x: Self) -> Self
      func ~>(_: Self, _: (_Abs, ())) -> Self
    }
    
    func ~><T : _CollectionType>(x: T, _: (_CountElements, ())) -> T.Index.Distance
    func ~><T : _CollectionType>(x: T, _: (_UnderestimateCount, ())) -> Int
    func ~><T : _CollectionType, R>(s: T, args: (_PreprocessingPass, ((T) -> R))) -> R?
    func ~><T : _SequenceType>(s: T, _: (_UnderestimateCount, ())) -> Int
    func ~><T : _SequenceType, R>(s: T, _: (_PreprocessingPass, ((T) -> R))) -> R?
    func ~><S : _Sequence_Type>(source: S, _: (_CopyToNativeArrayBuffer, ())) -> _ContiguousArrayBuffer<S.Generator.Element>
    func ~><C : CollectionType where C._Element == C._Element>(source: C, _: (_CopyToNativeArrayBuffer, ())) -> _ContiguousArrayBuffer<C._Element>
    func ~><T>(x: EmptyCollection<T>, _: (_CountElements, ())) -> Int
    func ~><T : _ForwardIndexType>(start: T, rest: (_Distance, T)) -> T.Distance
    func ~><T : _ForwardIndexType>(start: T, rest: (_Advance, T.Distance)) -> T
    func ~><T : _ForwardIndexType>(start: T, rest: (_Advance, (T.Distance, T))) -> T
    func ~><T : _BidirectionalIndexType>(start: T, rest: (_Advance, T.Distance)) -> T
    func ~><T : _BidirectionalIndexType>(start: T, rest: (_Advance, (T.Distance, T))) -> T
    func ~><T : _RandomAccessIndexType>(start: T, rest: (_Distance, (T))) -> T.Distance
    func ~><T : _RandomAccessIndexType>(start: T, rest: (_Advance, (T.Distance))) -> T
    func ~><T : _RandomAccessIndexType>(start: T, rest: (_Advance, (T.Distance, T))) -> T
    func ~><T : _SignedNumberType>(x: T, _: (_Abs, ())) -> T
    func ~><T : AbsoluteValuable>(x: T, _: (_Abs, ())) -> T
    func ~><T>(x: CollectionOfOne<T>, _: (_CountElements, ())) -> Int
    

    For example,

    42 ~> _advance(12) // -> 54
    42 ~> _distanceTo(23) // -> -19
    

    I don't know how these are being used, though :-/

    0 讨论(0)
提交回复
热议问题