Hi all,

The current behavior of generics in Swift causes it lose type information at 
compile time due to the desire of maintaining a single version of the function. 
This runs counter to how c++ works, which creates a new copy of a function per 
type, but preserves information to be preserved. This can cause unexpected 
behavior from the user’s perspective:

        protocol DispatchType {}
        class DispatchType1: DispatchType {}

        func doBar<D:DispatchType>(value:D) {    
                print(“General function called")
        }

        func doBar(value:DispatchType1) {
                print("DispatchType1 called")
        }

        func test<D:DispatchType>(value:D) {
                doBar(value: value)
        }

        test(value: d1)     // “General function called”, but it’s not obvious 
why


The suggested method to get around this issue is to use a protocol to create a 
witness table, allowing for runtime dispatch. However, this approach is not 
ideal in all cases because: (a) the overhead of runtime dispatch may not be 
desirable, especially because this is something that can be determined at 
compile time; and (b) there are some designs in which this behavior can 
complicate things.

One example of a design where this behavior can be problematic is when a 
protocol is used to determine what functions get dispatched:

        protocol Storage { … }
        class Tensor<S:Storage> { … }

        class CBlasStorage: Storage { … }
        class OpenCLStorage: Storage { … }

        func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> { … }

        // like behavior, these will not work if called from another generic 
function (but will work for non-generic functions)
        func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> 
where S:CBlasStorage { … }
        func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> 
where S:OpenCLStorage { … }

In this case, depending on the underlying storage, we want an optimized version 
of `dot` to be called. To make this work correctly we can add static methods to 
`Tensor`, but this has several drawbacks: (a) it makes the `Tensor` class 
monolithic, every possible method must be determine a priori and be defined in 
the class; (b) it doesn’t allow new methods to be added Tensor without touching 
the main class; and (c) it unnecessarily forces users to user the more verbose 
`Tensor.dot(a, b)`.

Point (a) in theory could be made better by creating a `TensorOps` protocols. 
However, because type constraints cannot currently be placed on extensions, it 
is not currently possible to implement.


One potential solution would be to add/extend an attribute for generic 
functions that would force multiple versions of that function to be created. 
There is already there is a `@_specialize` attribute, but you have to: (a) 
manually write out all the cases you want to cover; and (b) only affects the 
compiled code, which does not change this behavior. Due to the fact that 
`@_specialize` exists, I’m going to assume it wouldn’t be a major change to the 
language to extend the behavior to compile-time dispatch.


Thanks!
Abe
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to