[swift-evolution] Compile-time generic specialization

Abe Schneider abe.schneider at gmail.com
Fri Feb 10 11:05:55 CST 2017

Hi Joe,

> If there's really an independent implementation for each `S: Storage`, then you can make `tensorDot` a requirement of `Storage` and avoid the explosion that way. Ad-hoc type dispatch by either overloading or if chains should be a last resort when protocols really can't model what you're trying to do. Ad-hoc overloading wouldn't really save you any work compared to the if chain—you'd have all the exact problems you mentioned, having to add an overload for every new combo of types, but you'd also have to also think about the implicit relationships among the overloads according to the language's overloading rules instead of in explicit logic.

You are correct in the number of Impls I need (I was incorrect in that
statement). But I think the if-branches are still problematic. I may
need the same number of functions as branches, but I think the code is
cleaner/easier to read and maintain:

   func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S>
where S:CBlasStorage<Float> { .. }
   func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S>
where S:CBlasStorage<Double> { .. }

   // NativeStorage has no optimization per type, so we can lump all
of these into a single Impl
   func dot<T>(_ lhs:Tensor<NativeStorage<T>>, _
rhs:Tensor<NativeStorage<T>>) -> Tensor<NativeStorage<T>>  { .. }

The advantages from this approach are: (a) it has less repeated code
(i.e. I don't have to create both an Impl and an if-branch); (b)
adding a new storage type does require redefining some (if not all) of
the functions (though it provides a nice mechanism for dealing with
defaults), but that code can be kept as a separate module; and (c) You
are effectively rolling your own dynamic dispatch, which is something
I much rather leave up to the compiler to do.


More information about the swift-evolution mailing list