Hi Robert,

Sorry, I’m not sure I understand your question. In c++ you can do the following:

struct Storage {};
struct CBlasStorage: Storage {};

template <typename S> class Tensor {};

template <typename S>
Tensor<S> dot(const Tensor<S> &lhs, const Tensor<S> &rhs) {
  std::cout << "general version called" << std::endl;
  Tensor<S> result;
  return result;
}

// specialized version for CBlasStorage
template <>
Tensor<CBlasStorage> dot(const Tensor<CBlasStorage> &lhs, const 
Tensor<CBlasStorage> &rhs) {
  std::cout << "specialized version called" << std::endl;
  Tensor<CBlasStorage> result;
  return result;
}

// this preserves type information and will call the appropriate `dot`
template <typename T>
void doSomething(const Tensor<T> &lhs, const Tensor<T> &rhs) {
  auto result = dot(lhs, rhs);
}

int main(int argc, char **argv) {
  Tensor<CBlasStorage> a, b;
  doSomething(a, b); // we should get "specialized version called"
}


The potential equivalent for Swift could look like:

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

Which would cause the compile to create a version of `dot` per S type that it 
gets called with. Thus, when `doSomething` is called, it would dispatch to that 
version of `dot`, allowing the type information to be preserved in the same way 
it does in c++.

Abe

> On Feb 5, 2017, at 11:35 AM, Robert Widmann <[email protected]> wrote:
> 
> I don't understand how this change would cause method dispatch to invoke a 
> different prototype.  Specialization in either language mentioned doesn't do 
> that.
> 
> ~Robert Widmann
> 
> 2017/02/05 11:28、Abe Schneider via swift-evolution 
> <[email protected]> のメッセージ:
> 
>> 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

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

Reply via email to