implicitely set an associated type - swift

I'm trying to figure out how to implicitely set a Generic (type of a argument) in a class without changing the type of the whole class to something like SomeTestClass< SomeType>, where the owner of an object has to know the type of the generic.
Example
This example DOESNT work! This is how I would like it to work
protocol SomeTestProtocol {
associatedtype T
init(arg: T)
}
Don't want to use SomeTestClass< T>
because the class, which holds this class
wont know the type of the generics used
class SomeTestClass: SomeTestProtocol {
required init(arg: T) {
}
// Use T outside the init-scope
}
Note: The Protocol is just a try for a work-around! It isn't necessary for the final solution
So the main question is: How can I use T outside the init-scope in the class below without using a class-generic, which must be known when owning an object
class SomeTestClass2/*< T>*/ {
init(arg: T) {
}
// Use T outside the init-scope
}
Thanks for your help!

Important note, the T from associatedtype T and the T from init<T> can be different types. They are both defining a generic type with different scopes an could be different. If you want them to be the same, the init should be
init(arg: T)
If SomeTestClass is always going to use the same type, you can add
typealias T = Int // or something
or implement the init as
required init(arg: Int)
It works if you get rid of the associatedtype T from the protocol. Though this removes SomeTestClass.T form existence.

If you declare an associated type in a protocol, that type will become generic over different implementations of the protocol, but each conforming class will need to assign a concrete type to that associate type as a typealias (which can be done implicitly by declaring all variables/functions using the associated type with the same concrete type), so your conforming types won't be generic.
If you want to have generic methods in your conforming classes, you'll need to make the classes themselves generic.

If you only want access to T outside of init, then you just store T in a property:
class S {
let theType: Any.Type
init<T>(arg: T) {
theType = T.self
}
}
I suspect you actually want something different than what you've said you want. I suspect you want to store arg. But if so, what do you plan to do with arg if you have no idea what its type is? What methods can you call? What function could it be the return value of? Without resorting to as?, there's nothing you can do with T (and reliance on as? generally means you've misunderstood your types).
So you need to start with how you want T and SomeTestClass to be used. Without that, it's impossible to discuss how T should be stored (the storage is irrelevant if you never use it).

Related

What exactly is a metatype in Swift?

I am very confused around the concept of "metatype" in the Swift language.
Suppose I have:
class SomeClass {
class func callClassMethod() {
print("I'm a class method. I belong to my type.")
}
func callInstanceMethod() {
print("I'm an instance method. I belong to my type instance.")
}
}
According to the definition:
A metatype type refers to the type of any type, including class types,
structure types, enumeration types, and protocol types.
SomeClass is already a type called SomeClass, then what exactly is the type of SomeClass?
I can create a SomeClass.Type variable:
let var1 : SomeClass.Type = SomeClass.self
var1.doIt();//"I'm a class method. I belong to my type."
but I can also call the static/class function this way:
SomeClass.doIt();//"I'm a class method. I belong to my type."
Are they the same?
They are the same because the compiler guarantees that class names are unique (Swift is name spaced by module), so there is only one thing that is of SomeClass.Type and that is the class SomeClass. The meta type is often useful when you just want to pass the type of something to a function but you don't want to pass an instance. Codable does this for instance:
let decoded = try decoder.decode(SomeType.self, from: data)
If you could not pass the meta type here then the compiler could still infer the return type based on an annotation on the left, but it would be less readable:
let decoded: Sometype = try decoder.decode(data)
Some libraries do use the type inference style instead, although the Apple preference seems to be to use the meta type as its clearer what the right side of the assignment is on its own, without relying on type inference from the left side of the assignment.

Why don't protocols in Swift use brackets instead of associated types? [duplicate]

I'm confused about the difference between the syntax used for associated types for protocols, on the one hand, and generic types on the other.
In Swift, for example, one can define a generic type using something like
struct Stack<T> {
var items = [T]()
mutating func push(item: T) {
items.append(item)
}
mutating func pop() -> T {
return items.removeLast()
}
}
while one defines a protocol with associated types using something like
protocol Container {
associatedtype T
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
Why isn't the latter just:
protocol Container<T> {
mutating func append(item: T)
var count: Int { get }
subscript(i: Int) -> T { get }
}
Is there some deep (or perhaps just obvious and lost on me) reason that the language hasn't adopted the latter syntax?
RobNapier's answer is (as usual) quite good, but just for an alternate perspective that might prove further enlightening...
On Associated Types
A protocol is an abstract set of requirements — a checklist that a concrete type must fulfill in order to say it conforms to the protocol. Traditionally one thinks of that checklist of being behaviors: methods or properties implemented by the concrete type. Associated types are a way of naming the things that are involved in such a checklist, and thereby expanding the definition while keeping it open-ended as to how a conforming type implements conformance.
When you see:
protocol SimpleSetType {
associatedtype Element
func insert(_ element: Element)
func contains(_ element: Element) -> Bool
// ...
}
What that means is that, for a type to claim conformance to SimpleSetType, not only must that type contain insert(_:) and contains(_:) functions, those two functions must take the same type of parameter as each other. But it doesn't matter what the type of that parameter is.
You can implement this protocol with a generic or non-generic type:
class BagOfBytes: SimpleSetType {
func insert(_ byte: UInt8) { /*...*/ }
func contains(_ byte: UInt8) -> Bool { /*...*/ }
}
struct SetOfEquatables<T: Equatable>: SimpleSetType {
func insert(_ item: T) { /*...*/ }
func contains(_ item: T) -> Bool { /*...*/ }
}
Notice that nowhere does BagOfBytes or SetOfEquatables define the connection between SimpleSetType.Element and the type used as the parameter for their two methods — the compiler automagically works out that those types are associated with the right methods, so they meet the protocol's requirement for an associated type.
On Generic Type Parameters
Where associated types expand your vocabulary for creating abstract checklists, generic type parameters restrict the implementation of a concrete type. When you have a generic class like this:
class ViewController<V: View> {
var view: V
}
It doesn't say that there are lots of different ways to make a ViewController (as long as you have a view), it says a ViewController is a real, concrete thing, and it has a view. And furthermore, we don't know exactly what kind of view any given ViewController instance has, but we do know that it must be a View (either a subclass of the View class, or a type implementing the View protocol... we don't say).
Or to put it another way, writing a generic type or function is sort of a shortcut for writing actual code. Take this example:
func allEqual<T: Equatable>(a: T, b: T, c: T) {
return a == b && b == c
}
This has the same effect as if you went through all the Equatable types and wrote:
func allEqual(a: Int, b: Int, c: Int) { return a == b && b == c }
func allEqual(a: String, b: String, c: String) { return a == b && b == c }
func allEqual(a: Samophlange, b: Samophlange, c: Samophlange) { return a == b && b == c }
As you can see, we're creating code here, implementing new behavior — much unlike with protocol associated types where we're only describing the requirements for something else to fulfill.
TLDR
Associated types and generic type parameters are very different kinds of tools: associated types are a language of description, and generics are a language of implementation. They have very different purposes, even though their uses sometimes look similar (especially when it comes to subtle-at-first-glance differences like that between an abstract blueprint for collections of any element type, and an actual collection type that can still have any generic element). Because they're very different beasts, they have different syntax.
Further reading
The Swift team has a nice writeup on generics, protocols, and related features here.
This has been covered a few times on the devlist. The basic answer is that associated types are more flexible than type parameters. While you have a specific case here of one type parameter, it is quite possible to have several. For instance, Collections have an Element type, but also an Index type and a Generator type. If you specialized them entirely with type parameterization, you'd have to talk about things like Array<String, Int, Generator<String>> or the like. (This would allow me to create arrays that were subscripted by something other than Int, which could be considered a feature, but also adds a lot of complexity.)
It's possible to skip all that (Java does), but then you have fewer ways that you can constrain your types. Java in fact is pretty limited in how it can constrain types. You can't have an arbitrary indexing type on your collections in Java. Scala extends the Java type system with associated types just like Swift. Associated types have been incredibly powerful in Scala. They are also a regular source of confusion and hair-tearing.
Whether this extra power is worth it is a completely different question, and only time will tell. But associated types definitely are more powerful than simple type parameterization.
To add to the already great answers, there's another big difference between generics and associated types: the direction of the type generic fulfilment.
In case of generic types, it's the client that dictates which type should be used for the generic, while in case of protocols with associated types that's totally in the control of the type itself. Which means that types that conform to associated types are in liberty to choose the associated type that suits them best, instead of being forced to work with some types they don't know about.
As others have said, the Collection protocol is a good example of why associated types are more fit in some cases. The protocol looks like this (note that I omitted some of the other associated types):
protocol Collection {
associatedtype Element
associatedtype Index
...
}
If the protocol would've been defined as Collection<Element, Index>, then this would've put a great burden on the type conforming to Collection, as it would've have to support any kind of indexing, many of them which don't even make sense (e.g. indexing by a UIApplication value).
So, choosing the associated types road for protocol generics it's also a matter of empowering the type that conforms to that protocol, since it's that type the one that dictates what happens with the generics. And yes, that might sound less flexible, but if you think about it all types that conform to Collection are generic types, however they only allow generics for the types that make sense (i.e. Element), while "hardcoding" the other associated types (e.g. Index) to types that make sense and are usable in their context.

Swift protocol used as type in method not accepting instances of same protocol

I've encountered a problem with a method which I cannot explain. Here is some test code which shows the problem:
protocol Base {}
protocol Extended: Base {}
struct Instance:Extended {}
let anInstance = Instance()
let instanceOfBase = anInstance as Base
let instanceOfExtended = anInstance as Extended
func aMethod<T:Base>(_ instance:T) {}
aMethod(anInstance)
aMethod(instanceOfBase) // Error - Cannot invoke 'aMethod' with an argument list of type '(Base)'
aMethod(instanceOfExtended) // Error - Cannot invoke 'aMethod' with an argument list of type '(Extended)'
According to the Apple doco I've read on protocols, generics, etc. aMethod() should accept any object that conforms to the Base protocol. Yet it rejects both instances where I have cast them to either Base or Extended.
Can anyone explain this?
Also:
func aMethod2(_ instance:Base) {}
aMethod2(anInstance)
aMethod2(instanceOfBase)
aMethod2(instanceOfExtended)
Works fine so the difference seems to be whether the instance argument is based (excuse the pun) on Base or <T:Base>.
For anyone questioning why I would declare a generic here. The original code looked like this:
func addViewController<T:ModelObject>(_ stack:inout [UIViewController],
object:T?,
controller:DetailsViewController<T>?,
storyboardId:String) {...
As you can see, I want to constrain several arguments to the same type. Hence the use of a generic rather than just specifying base.
T is a constraint on a concrete type
<T:Base> is a constraint on the placeholder type T. T must be a concrete type (a class, enum or struct such as Instance) that conforms to the Base protocol. The placeholder type T cannot be the Base protocol.
aMethod<T:Base>(:) must be called with a variable that is of a type that conforms to the Base protocol at compile time. aMethod<T:Base>(:) cannot be called with a variable that is only known to be of type Base.
The following line of code instantiates a variable named anInstance that is of struct type Instance.
let anInstance = Instance()
aMethod(anInstance) compiles because anInstance is of a concrete type Instance that conforms to the Base protocol.
The following line of code instantiates a variable named instanceOfBase that is of protocol type Base.
let instanceOfBase: Instance = anInstance as Base
aMethod(instanceOfBase) does not compile because instanceOfBase is not of a concrete type that conforms to the Base protocol. It is of the protocol type Base.
Here is another snippet that fails to illustrate the issue. In this case the base argument is only known to be of the protocol type Base.
func aMethod(base: Base) {
aMethod(base) // Cannot invoke 'aMethod' with an argument list of type '(Base)'
}
Hmm, I don't really get why you're doing this.
Let's say:
you want aMethod to accept any instance that conforms to Base protocol, then you can just make it func aMethod(_ instance: Base) {}
You want preserve type information inside the function then you use the generic method but pass in an instance of a concrete implementation of the protocol.
Please note that func aMethod<T:Base>(_ instance:T) {} means it's expecting an instance of type T that conforms Base. Neither Base or Extended is a valid type.
What you're doing here is using generics without its power...Which, IMHO, doesn't make sense. Unless you can provide a more realistic scenario?

What is the in-practice difference between generic and protocol-typed function parameters?

Given a protocol without any associated types:
protocol SomeProtocol
{
var someProperty: Int { get }
}
What is the difference between these two functions, in practice (meaning not "one is generic and the other is not")? Do they generate different code, do they have different runtime characteristics? Do these differences change when the protocol or functions become non-trivial? (since a compiler could probably inline something like this)
func generic<T: SomeProtocol>(some: T) -> Int
{
return some.someProperty
}
func nonGeneric(some: SomeProtocol) -> Int
{
return some.someProperty
}
I'm mostly asking about differences in what the compiler does, I understand the language-level implications of both. Basically, does nonGeneric imply a constant code size but slower dynamic dispatch, vs. generic using a growing code size per type passed, but with fast static dispatch?
(I realise that OP is asking less about the language implications and more about what the compiler does – but I feel it's also worthwhile also to list the general differences between generic and protocol-typed function parameters)
1. A generic placeholder constrained by a protocol must be satisfied with a concrete type
This is a consequence of protocols not conforming to themselves, therefore you cannot call generic(some:) with a SomeProtocol typed argument.
struct Foo : SomeProtocol {
var someProperty: Int
}
// of course the solution here is to remove the redundant 'SomeProtocol' type annotation
// and let foo be of type Foo, but this problem is applicable anywhere an
// 'anything that conforms to SomeProtocol' typed variable is required.
let foo : SomeProtocol = Foo(someProperty: 42)
generic(some: something) // compiler error: cannot invoke 'generic' with an argument list
// of type '(some: SomeProtocol)'
This is because the generic function expects an argument of some type T that conforms to SomeProtocol – but SomeProtocol is not a type that conforms to SomeProtocol.
A non-generic function however, with a parameter type of SomeProtocol, will accept foo as an argument:
nonGeneric(some: foo) // compiles fine
This is because it accepts 'anything that can be typed as a SomeProtocol', rather than 'a specific type that conforms to SomeProtocol'.
2. Specialisation
As covered in this fantastic WWDC talk, an 'existential container' is used in order to represent a protocol-typed value.
This container consists of:
A value buffer to store the value itself, which is 3 words in length. Values larger than this will be heap allocated, and a reference to the value will be stored in the value buffer (as a reference is just 1 word in size).
A pointer to the type's metadata. Included in the type's metadata is a pointer to its value witness table, which manages the lifetime of value in the existential container.
One or (in the case of protocol composition) multiple pointers to protocol witness tables for the given type. These tables keep track of the type's implementation of the protocol requirements available to call on the given protocol-typed instance.
By default, a similar structure is used in order to pass a value into a generic placeholder typed argument.
The argument is stored in a 3 word value buffer (which may heap allocate), which is then passed to the parameter.
For each generic placeholder, the function takes a metadata pointer parameter. The metatype of the type that's used to satisfy the placeholder is passed to this parameter when calling.
For each protocol constraint on a given placeholder, the function takes a protocol witness table pointer parameter.
However, in optimised builds, Swift is able to specialise the implementations of generic functions – allowing the compiler to generate a new function for each type of generic placeholder that it's applied with. This allows for arguments to always be simply passed by value, at the cost of increasing code size. However, as the talk then goes onto say, aggressive compiler optimisations, particularly inlining, can counteract this bloat.
3. Dispatch of protocol requirements
Because of the fact that generic functions are able to be specialised, method calls on generic arguments passed in are able to be statically dispatched (although obviously not for types that use dynamic polymorphism, such as non-final classes).
Protocol-typed functions however generally cannot benefit from this, as they don't benefit from specialisation. Therefore method calls on a protocol-typed argument will be dynamically dispatched via the protocol witness table for that given argument, which is more expensive.
Although that being said, simple protocol-typed functions may be able to benefit from inlining. In such cases, the compiler is able to eliminate the overhead of the value buffer and protocol and value witness tables (this can be seen by examining the SIL emitted in a -O build), allowing it to statically dispatch methods in the same way as generic functions. However, unlike generic specialisation, this optimisation is not guaranteed for a given function (unless you apply the #inline(__always) attribute – but usually it's best to let the compiler decide this).
Therefore in general, generic functions are favoured over protocol-typed functions in terms of performance, as they can achieve static dispatch of methods without having to be inlined.
4. Overload resolution
When performing overload resolution, the compiler will favour the protocol-typed function over the generic one.
struct Foo : SomeProtocol {
var someProperty: Int
}
func bar<T : SomeProtocol>(_ some: T) {
print("generic")
}
func bar(_ some: SomeProtocol) {
print("protocol-typed")
}
bar(Foo(someProperty: 5)) // protocol-typed
This is because Swift favours an explicitly typed parameter over a generic one (see this Q&A).
5. Generic placeholders enforce the same type
As already said, using a generic placeholder allows you to enforce that the same type is used for all parameters/returns that are typed with that particular placeholder.
The function:
func generic<T : SomeProtocol>(a: T, b: T) -> T {
return a.someProperty < b.someProperty ? b : a
}
takes two arguments and has a return of the same concrete type, where that type conforms to SomeProtocol.
However the function:
func nongeneric(a: SomeProtocol, b: SomeProtocol) -> SomeProtocol {
return a.someProperty < b.someProperty ? b : a
}
carries no promises other than the arguments and return must conform to SomeProtocol. The actual concrete types that are passed and returned do not necessarily have to be the same.
If your generic method had more than one parameter involving T, there would be a difference.
func generic<T: SomeProtocol>(some: T, someOther: T) -> Int
{
return some.someProperty
}
In the method above, some and someOther have to be the same type. They can be any type that conforms to SomeProtocol, but they have to be the same type.
However, without generics:
func nonGeneric(some: SomeProtocol, someOther: SomeProtocol) -> Int
{
return some.someProperty
}
some and someOther can be different types, as long as they conform to SomeProtocol.

How to pass protocol with associated type (generic protocol) as parameter in Swift?

I have to pass an interface as a parameter to a function. Interface is generic a.k.a. has a associated type. I couldn't find a good way to do that. Here is my code:
protocol IObserver : class {
typealias DelegateT
...
}
class Observer: IObserver {
typealias DelegateT = IGeneralEventsDelegate // IGeneralEventsDelegate is a protocol
...
}
func notify(observer: IObserver) { ... } // here I need a type for observer param
I found that this will work:
func notify<T: IObserver where T.DelegateT == IGeneralEventsDelegate>(observer: T) { ... }
, but come on that is too complicated. What if I want to save this param in class variable, should I make the whole class generic, just because of this function.
It is true that I'm C++ developer and I'm new to the Swift language, but the way the things are done are far too complicated and user unfriendly ... or I'm too stupid :)
If you use typealias in a protocol to make it generic-like, then you cannot use it as a variable type until the associated type is resolved. As you have probably experienced, using a protocol with associated type to define a variable (or function parameter) results in a compilation error:
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self os associated type requirements
That means you cannot use it as a concrete type.
So the only 2 ways I am aware of to use a protocol with associated type as a concrete type are:
indirectly, by creating a class that implements it. Probably not what you have planned to do
making explicit the associated type like you did in your func
See also related answer https://stackoverflow.com/a/26271483/148357