Why is generic specialization lost inside a generic function
Specialization is not a replacement for inheritance. It should be used to improve performance, not change behavior.
For example, distance(from:to:)
is usually O(k), where k is the distance. For RandomAccessCollection it can be performed in O(1) due to a specialization. But the result is the same either way.
Specialization is done at compile-time based on the information the compiler has. In your example, the compiler can see that boolArray
is a [Bool]
, and so it uses the specialized extension. But inside of isBool
, all that the compiler knows is that array
is an Array. It doesn't know when compiling the function what kind of Array will be passed. So it picks the more general version to cover all cases.
(The compiler may create multiple versions of isBool
in the binary for optimization purposes, but luckily I haven't found any situations where this impacts what extensions or overloads are called. Even if it actually creates an inlined, Bool-specific version of isBool
, it will still use the more general Array extension. That's a good thing.)
Leaving your extensions in place, the following would do what you expect (though I don't encourage this):
func isBool<Element>(_ array: [Element]) -> Bool {
array.isBool
}
func isBool(_ array: [Bool]) -> Bool {
array.isBool
}
Now isBool
is overloaded and the most specific one will be selected. Within the context of the second version, array
is known to be [Bool]
, and so the more specialized extension will be selected.
Even though the above works, I would strongly recommend against using specialized extensions or ambiguous overloads that change behavior. It is fragile and confusing. If isBool()
is called in the context of another generic method where Element is not known, it again may not work as you expect.
Since you want to base this on the runtime types, IMO you should query the type at runtime using is
. That gets rid of all the ambiguity. For example:
extension Array {
var isBool: Bool { Element.self is Bool.Type }
}
func isBool<Element>(_ array: [Element]) -> Bool {
array.isBool
}
You can make this much more flexible and powerful by adding a protocol:
protocol BoolLike {}
extension Array {
var isBool: Bool { Element.self is BoolLike.Type }
}
Now, any types you want to get "bool-like" behavior just need to conform:
extension Bool: BoolLike {}
This allows you all the flexibility of your extensions (i.e. the isBool
code doesn't need to know all the types), while ensuring the behavior is applied based on runtime types rather than compile-time types.
Just in case it comes up, remember that protocols do not conform to themselves. So [BoolLike]
would return isBool == false
. The same is true for an extension with where Element: BoolLike
. If you need that kind of thing to work, you need to deal with it explicitly.
extension Array {
var isBool: Bool {
Element.self is BoolLike.Type || Element.self == BoolLike.self
}
}
Why is specialization in C# generics limited?
What does it mean by "specialization"? Is it not the same as instantiation of a generic type with a specific type argument?
Author explains in the portion of his answer dedicated to Java generics that
specialization of a generic type [is] the ability to use specialized source code for any particular generic argument combination.
In other words, it is an ability to do something special if a generic type parameter is of a specific type. Supplying an implementation of List<T>
that represents individual elements as bits when you instantiate the type as List<bool>
would be an example of specialization.
What does it mean by "the degree of specialization is limited"?
Author means that although you can write things like
if (typeof(T) == typeof(bool)) {
...
}
your abilities to respond to a combination of type arguments are limited, because any decision on a type combination has to be made at run-time.
Why is it "a result of the fact that a generic type definition is compiled before any reification happens"?
Because reification is done in CLR, well after C# compiler is out of the picture. The compiler must produce a generic type definition for CLR to use as a "template" for making closed constructed types for instances of a generic class.
Why is generic type information lost in generic functions with a Comparable constraint?
It's not the String
. Your closure { $0.hasPrefix("A") }
has the return type Bool
, which is assigned to U
. Bool
is Equatable
, but not Comparable
.
You probably want the closure to return Bool
, but selectComparable
to return U
.
Edit
Here's evidence that returning a String
(which is Comparable
) instead of a Bool
(not Comparable
) will compile:
func selectComparable<T: Comparable, U: Comparable>(x:T, f:(T) -> U) -> U {
return f(x)
}
var b4 = selectComparable("ABC") { (str: String) -> String in str }
Swift - Cannot explicitly specialize a generic function
Give the closure an explicit type to fix T
:
let task = AsyncTask.background{ (progress: Float -> Void, fulfill: MyAwesomeObject -> Void, reject: NSError -> Void, configure: SwiftTask.TaskConfiguration) -> Void in
let obj = MyAwesomeObject()
//-- ... do work here
fulfill(obj)
}
How to implement specialized versions of a generic function?
Remember that you always implement a trait for something. Therefore, trait implementation must always contain for
clause:
impl SomeTrait for Something
If there is no for
, then it is not a trait implementation. In your case impl Foo<i32>
is not an implementation of Foo
for i32
or whatever you think it is; it is an inherent method declaration clause on the bare trait object type Foo<i32>
.
What you actually want is possible to do using Self
type parameter:
trait Foo {
fn foo(a: Self, b: Self, c: Self);
}
impl Foo for i32 {
fn foo(a: i32, b: i32, c: i32) {}
}
impl Foo for i16 {
fn foo(a: i16, b: i16, c: i16) {}
}
fn main() {
Foo::foo(1i32,2,3);
Foo::foo(1i16,2,3);
}
This code works.
Note that now Foo
is implemented for a certain type. The type a trait is implemented for is available via the implicit Self
type parameter, and you can see how it is used in foo()
declaration.
Related Topics
Difference Between Text("") and Text(Verbatim: "") Initializers in Swiftui
Avcapturevideodataoutput Captureoutput Not Being Called
How to Make a Extension for Array of Specific Type in Swift
Core Data Predicate Not Working
Adding Items to Array as a Dictionary Value
How to Find the Operator Definition in Swift
Nsjsonserialization Not Working as Expected in a Playground
Can You Override Nsdateformatter 12 VS 24 Hour Time Format Without Using a Custom Dateformat
Xcode 6.3 Code Completion Too Slow
Swift: How to Get Everything After a Certain Set of Characters
Show Nsmenu Only on Nsstatusbarbutton Right Click
Using Applescript with Apple Events in MACos - Script Not Working
What Makes a Property a Computed Property in Swift
Physicsbody: Could Not Create Physics Body
Swift: How to Get Form Values Using Eureka Form Builder
A Switch Bug in Swift? - "Switch Must Be Exhaustive, Consider Adding a Default Clause."