Can I somehow force the generic type to have a generic type ?
I want to have some functions, that have as a parameter type U, so how can I do that ?
Code:
class TableViewViewModel<T<U>> {
typealias SectionDataType = T
typealias RowDataType = U
var sections = [SectionDataType<RowDataType>]()
}
Try declaring a protocol SectionDataType that requires an associated type. This means that any conforming type (like Section below) must implement a typealias. Then, in TableViewViewModel you can access the type which you were calling U through that typealias.
protocol SectionDataType {
associatedtype RowDataType
}
struct Section<U>: SectionDataType {
typealias RowDataType = U
}
class TableViewViewModel<T: SectionDataType> {
typealias RowDataType = T.RowDataType
var sections = [T]()
}
All you have to do with a generic, is declare the types (what you are doing with the angle brackets). After that, it's syntax-as-usual. I'm not sure exactly what you want to do, so I'll just post an example. The following function declares three generic types (T, U, Z), takes a type U and a function as parameters, the function itself takes a value of type U and returns a value of type T. All of this returns a value of type Z. (This is a pretty useless func as-is, but an example of how to use generics in a complex fashion):
func myFunc<U,T,Z>(curry : (U) -> T, value : U) -> Z {
return curry(value) as! Z
}
Related
I was working with Swinject and a problem is bugging me. I have been stuck one this for almost an entire day. I suspect this is due to Swift being a statictly typed language but I'm not entirely sure.
I summarized my problem in this playground
protocol Protocol {}
class Class: Protocol {}
let test: Protocol.Type = Class.self
func printType(confromingClassType: Protocol.Type) {
print(confromingClassType)
}
func printType<Service>(serviceType: Service.Type) {
print(serviceType)
}
print(Class.self) // "Class"
printType(serviceType: Class.self) // "Class"
print(test) // "Class"
printType(confromingClassType: test) // "Class"
printType(serviceType: test) // "note: expected an argument list of type '(serviceType: Service.Type)'"
I tried different solutions like test.self or type(of: test) but none of them work.
So I guess I can't call a function with a generic parameter provided as a variable ?
P.Type vs. P.Protocol
There are two kinds of protocol metatypes. For some protocol P, and a conforming type C:
A P.Protocol describes the type of a protocol itself (the only value it can hold is P.self).
A P.Type describes a concrete type that conforms to the protocol. It can hold a value of C.self, but not P.self because protocols don't conform to themselves (although one exception to this rule is Any, as Any is the top type, so any metatype value can be typed as Any.Type; including Any.self).
The problem you're facing is that for a given generic placeholder T, when T is some protocol P, T.Type is not P.Type – it is P.Protocol.
So if we jump back to your example:
protocol P {}
class C : P {}
func printType<T>(serviceType: T.Type) {
print(serviceType)
}
let test: P.Type = C.self
// Cannot invoke 'printType' with an argument list of type '(serviceType: P.Type)'
printType(serviceType: test)
We cannot pass test as an argument to printType(serviceType:). Why? Because test is a P.Type; and there's no substitution for T that makes the serviceType: parameter take a P.Type.
If we substitute in P for T, the parameter takes a P.Protocol:
printType(serviceType: P.self) // fine, P.self is of type P.Protocol, not P.Type
If we substitute in a concrete type for T, such as C, the parameter takes a C.Type:
printType(serviceType: C.self) // C.self is of type C.Type
Hacking around with protocol extensions
Okay, so we've learnt that if we can substitute in a concrete type for T, we can pass a C.Type to the function. Can we substitute in the dynamic type that the P.Type wraps? Unfortunately, this requires a language feature called opening existentials, which currently isn't directly available to users.
However, Swift does implicitly open existentials when accessing members on a protocol-typed instance or metatype (i.e it digs out the runtime type and makes it accessible in the form of a generic placeholder). We can take advantage of this fact in a protocol extension:
protocol P {}
class C : P {}
func printType<T>(serviceType: T.Type) {
print("T.self = \(T.self)")
print("serviceType = \(serviceType)")
}
extension P {
static func callPrintType/*<Self : P>*/(/*_ self: Self.Type*/) {
printType(serviceType: self)
}
}
let test: P.Type = C.self
test.callPrintType()
// T.self = C
// serviceType = C
There's quite a bit of stuff going on here, so let's unpack it a little bit:
The extension member callPrintType() on P has an implicit generic placeholder Self that's constrained to P. The implicit self parameter is typed using this placeholder.
When calling callPrintType() on a P.Type, Swift implicitly digs out the dynamic type that the P.Type wraps (this is the opening of the existential), and uses it to satisfy the Self placeholder. It then passes this dynamic metatype to the implicit self parameter.
So, Self will be satisfied by C, which can then be forwarded onto printType's generic placeholder T.
Why is T.Type not P.Type when T == P?
You'll notice how the above workaround works because we avoided substituting in P for the generic placeholder T. But why when substituting in a protocol type P for T, is T.Type not P.Type?
Well, consider:
func foo<T>(_: T.Type) {
let t: T.Type = T.self
print(t)
}
What if we substituted in P for T? If T.Type is P.Type, then what we've got is:
func foo(_: P.Type) {
// Cannot convert value of type 'P.Protocol' to specified type 'P.Type'
let p: P.Type = P.self
print(p)
}
which is illegal; we cannot assign P.self to P.Type, as it's of type P.Protocol, not P.Type.
So, the upshot is that if you want a function parameter that takes a metatype describing any concrete type that conforms to P (rather than just one specific concrete conforming type) – you just want a P.Type parameter, not generics. Generics don't model heterogenous types, that's what protocol types are for.
And that's exactly what you have with printType(conformingClassType:):
func printType(conformingClassType: P.Type) {
print(conformingClassType)
}
printType(conformingClassType: test) // okay
You can pass test to it because it has a parameter of type P.Type. But you'll note this now means we cannot pass P.self to it, as it is not of type P.Type:
// Cannot convert value of type 'P.Protocol' to expected argument type 'P.Type'
printType(conformingClassType: P.self)
I've ran your code in a playground, and it seems that this is the reason why it wont compile
let test: Protocol.Type = Class.self
If you remove the type declaration for test, the code will work and will print out Class.Type at line 15.
So the following code compiles and runs:
protocol Protocol {}
class Class: Protocol {}
let test = Class.self
func printType<Service>(serviceType: Service.Type) {
print(serviceType)
}
print(Class.Type.self) // "Class.Type"
printType(serviceType: Class.Type.self) // "Class.Type"
print(type(of: test)) // "Class.Type"
printType(serviceType: type(of: test)) // "Class.Type"
I hope this helps with your problem.
Edit
The error I am getting in the playground with the original code is the following:
Playground execution failed: error: Untitled Page 2.xcplaygroundpage:9:1: error: cannot invoke 'printType' with an argument list of type '(serviceType: Protocol.Type.Type)'
printType(serviceType: type(of: test)) // "Class.Type"
This means you are calling Type 2 times, that's why the code will not compile, because the type you are already calling the method with the argument of type Protocol.Type.
If you change the method's signature like this:
let test: Protocol.Type = Class.self
func printType<Service>(serviceType: Service) {
print(serviceType)
}
everything will compile and work right, printing Class.type
This is also the reason my first version of the answer will compile, since it will assign the right type for test can call .Type only once.
I am attempting to create a function that can return any type. I do not want it to return an object of type Any, but of other types, i.e. String, Bool, Int, etc. You get the idea.
You can easily do this using generics in this fashion:
func example<T>(_ arg: T) -> T {
// Stuff here
}
But is it possible to do it without passing in any arguments of the same type? Here is what I am thinking of:
func example<T>() -> T {
// Stuff here
}
When I try to do this, everything works until I call the function, then I get this error:
generic parameter 'T' could not be inferred
is it possible to do it without passing in any arguments of the same type?
The answer is yes, but there needs to be a way for the compiler to infer the correct version of the generic function. If it knows what it is assigning the result to, it will work. So for instance, you could explicitly type a let or var declaration. The below works in a playground on Swift 3.
protocol Fooable
{
init()
}
extension Int: Fooable {}
extension String: Fooable {}
func foo<T: Fooable>() -> T
{
return T()
}
let x: String = foo() // x is assigned the empty string
let y: Int = foo() // y is assigned 0
I am trying to get the code below working in a playground:
public protocol SomeTypeProtocol {
associatedtype T
func convertTo<TNew:SomeTypeProtocol>()-> TNew
}
public class SomeClass<T>: SomeTypeProtocol{
var item:T
public init(item:T)
{
self.item = item
}
public func convertTo<TNew:SomeTypeProtocol where TNew.T : T>()-> TNew
{
return SomeClass<TNew.T>(item: item as! TNew.T)
}
}
Basically, I have a protocol with an associated type T, and a class conforming to that protocol with generic type T, and I need to implement the convertTo function that simply converts the class's type T to another type TNew.T which should be a subclass from T.
I have this implemented in other languages like C# and Java, so I am not sure why I can't get it working in Swift.
I am getting the following errors:
1) error: type 'TNew.T' constrained to non-protocol type 'T'
public func convertTo<TNew:SomeTypeProtocol where TNew.T : T>()-> TNew
2) error: cannot invoke initializer for type 'SomeClass<TNew.T>' with an argument list of type '(item: TNew.T)'
return SomeClass<TNew.T>(item: item as! TNew.T)
3) note: expected an argument list of type '(item: T)'
return SomeClass<TNew.T>(item: item as! TNew.T)
4)note: protocol requires nested type 'T'
associatedtype T
TNew.T : T
This is where the problem lies.
The colon, :, is used to declare a conformance constraint, not equality. You need to rewrite this as TNew.T == T if you want to equate the two types TNew.T and T.
I believe the closest you can get to doing something like that is this:
public func convertTo<TNew where TNew : T>()-> SomeClass<TNew>
{
return SomeClass<TNew>(item: item as! TNew)
}
But I don't believe you can't enforce it being only a subclass and not the same class.
Be careful doing this type of thing with generics. If you're needing to constantly change the Type of your generic, you might want to consider using a different design.
I have a generic class that can be initialised as any type. I would like to add a function with a single parameter that takes a value that is both of the class's generic type and conforms to the Comparable protocol. Type conformance should be enforced pre-compile.
I would like to do something like this:
class Object<T> {
let value: T!
init (value: T) {
self.value = value
}
func doSomething<U where U: Comparable, U == T>(otherValue: U) {
// do something
}
}
Is this possible to do?
Unfortunately, no. You can't further specialize a generic type in a method - you'd need to add a top-level function for the behavior you want.
This is the reason Array doesn't have a pure myArray.sort() function, since there's no way to guarantee that the members of any Array instance will be Comparable. Instead, there's a top-level function with this signature:
func sort<T : Comparable>(inout array: [T])
Your top-level function would have a similar structure:
func doSomething<T: Comparable)(obj: Object<T>, otherValue: T) {
// ...
}
For reference, this is possible since Swift 2.0:
extension Object where T : Comparable {
func doSomething(otherValue: T) {
// do something
}
}
The following code gives me an error on line return p.foo(self). The error says: 'P' does not have a member named 'foo'.
protocol P {
typealias T
func foo(c: C<T>) -> T
func foo2() -> T
}
class C<T> {
var p: P
init (p: P) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}
The protocol P defines an associated type which should match correctly with any specialized C type. Am I missing something? Or not?
I'll rename the types a bit before answering the question to make the problem a bit clearer:
protocol P {
typealias ElementType
func foo(c: C<ElementType>) -> ElementType
func foo2() -> ElementType
}
class C<T> {
var p: P
init (p: P) {
self.p = p
}
func bar() -> T {
return p.foo(self)
}
}
In that case you get three compiler errors:
error: <EXPR>:8:12: error: protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements
var p: P
^
<EXPR>:9:14: error: protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements
init (p: P) {
^
<EXPR>:13:16: error: 'P' does not have a member named 'foo'
return p.foo(self)
^ ~~~
The interesting one is the first/second one (they point out the same problem): "protocol 'P' can only be used as a generic constraint because it has Self or associated type requirements".
So the problem is the associated type. In the current configuration, you specify that the parameter for the initializer and the variable are of type P. But because you specified an associated type for P, that type is not specific enough to be used as a proper type. Only subtypes that actually specify what the ElementType is can be used. However, you can specific a generic parameter that has to be a subtype of P. In the case of the initializer you could write
init <S:P>(p: S) {
self.p = p
}
which would eliminate the compiler error for the initializer. Now the compiler knows the parameter has to be a subtype of P and a valid subtype always specifies what the ElementType is, so it is happy.
But this doesn't help you with this line:
var p: P
You still can't use the incomplete type P here. You would probably want to use S, but at the moment there is no connection between the S in the initializer and an S you would use as the type for you variable but they obviously need to be the same.
Time to introduce a second generic parameter to your class:
class C<T, S:P> {
var p: S
init (p: S) {
self.p = p
}
func bar() -> T {
return p.foo(self)
}
}
Almost done, now you have a properly specified type to use for your variable. But no your protocol specification is incorrect:
func foo(c: C<ElementType>) -> ElementType
C now takes two parameters and you need to specify these here. We would like to use `C here, but we can't:
error: :3:17: error
: type 'P' does not conform to protocol 'P'
func foo(c: C<ElementType, P>) -> ElementType
^
<EXPR>:2:15: note: associated type 'ElementType' prevents protocol from conforming to itself
typealias ElementType
Since P does not specify the associated type ElementType it does not properly conform to P and can't be used in a place where a type conforming to P is needed. But there is a nice special type: Self. That references the actual type of the implementing protocol, so we can write the following:
protocol P {
typealias ElementType
func foo(c: C<ElementType, Self>) -> ElementType
func foo2() -> ElementType
}
Now we specified that the foo-function that is implemented by any confirming type actually takes a C with the specified ElementType and the implementing type itself. Fancy, isn't it?
But we aren't fully done yet, one last error remains:
error: <EXPR>:13:18: error: cannot convert the expression's type 'T' to type 'S.ElementType'
return p.foo(self)
At this point the compiler knows the following:
p.foo(self) returns something of the ElementType of S
The function bar() should return something of type T
But there is nothing to tell it, that ElementType and T are actually the same, so it can't be sure whether this works and complains. So what we actually want is that the ElementType of S is always the same as T and we can specify this:
class C<T, S:P where S.ElementType == T> {
Complete code:
protocol P {
typealias ElementType
func foo(c: C<ElementType, Self>) -> ElementType
func foo2() -> ElementType
}
class C<T, S:P where S.ElementType == T> {
var p: S
init (p: S) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}
You never required that the two Ts match. They are each in their own scope. Therefore the compiler looks for a foo function with the wrong parameter types.
I believe something like this would be correct:
protocol P {
typealias T
func foo<ConcreteP>(c: C<T, ConcreteP>) -> T
func foo2() -> T
}
class C<T, ConcreteP: P where T == ConcreteP.T> {
var p: ConcreteP
init (p: ConcreteP) {
self.p = p
}
func bar() -> T {
return p.foo(self);
}
}
At least I don't get any syntax errors. However on compilation I get:
error: unimplemented IR generation feature non-fixed class layout
var p: ConcreteP
^
LLVM ERROR: unimplemented IRGen feature! non-fixed class layout
That sounds like a compiler bug.
From The Swift Programming Language:
Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code.
In order to use the .foo method, you need a struct or class that implements your protocol.