Swift: Method overriding in parameterized class - swift

I'm very new to Swift but I have some experience with OO-programming. I've started to try and use parameterized classes in Swift and I have come across a strange design feature when overloading methods. If I define the following classes:
class ParameterClassA {
}
class ParameterClassB: ParameterClassA {
}
class WorkingClassA<T: ParameterClassA> {
func someFunction(param: T) -> Void {
}
}
class WorkingClassB: WorkingClassA<ParameterClassB> {
override func someFunction(param: ParameterClassA) {
}
}
Then the code compiles fine. However, as you'll notice, I've overloaded the function that normally uses the parameter type, which in my example is ParameterClassB, and given it a parameter of type ParameterClassA. How is that supposed to work? I know that it's not allowed in Java, and I'm wondering how the type parameter is interpreted. Can it be anything from the class hierarchy of the type parameter?
Also note that the problem is exactly the same if I remove the type parameter constraint : ParameterClassA in WorkingClassA.
If I remove the override keyword, then I get a compiler error requesting that I add it.
Thanks a lot for any explanation!

It has nothing at all to do with the generics (what you call "parameterized"). It has to do with how one function type is substitutable for another in Swift. The rules is that function types are contravariant with respect to their parameter types.
To see this more clearly, it will help to throw away all the misleading generic stuff and the override stuff, and instead concentrate directly on the business of substituting one function type for another:
class A {}
class B:A {}
class C:B {}
func fA (x:A) {}
func fB (x:B) {}
func fC (x:C) {}
func f(_ fparam : B -> Void) {}
let result1 = f(fB) // ok
let result2 = f(fA) // ok!
// let result3 = f(fC) // not ok
We are expected to pass to the function f as its first parameter a function of type B -> Void, but a function of type A -> Void is acceptable instead, where A is superclass of B.
But a function of type C -> Void is not acceptable, where C is subclass of B. Functions are contravariant, not covariant, on their parameter types.

#matt is completely right about why this works – it's due to the fact that method inputs are contravariant, instead of covariant.
This means that you can only override a given function with another function that has a broader (or the same) input type – meaning that you can substitute in a superclass argument in place of a subclass argument. This may seem completely backwards at first – but it makes total sense if you think about it a bit.
The way I would explain it in your situation is with a slightly stripped down version of your code:
class ParameterClassA {}
class ParameterClassB: ParameterClassA {}
class WorkingClassA {
func someFunction(param: ParameterClassB) {}
}
class WorkingClassB: WorkingClassA {
override func someFunction(param: ParameterClassA) {}
}
Note here that WorkingClassB is overriding the someFunction with ParameterClassA – the superclass of ParameterClassB.
Now, imagine you have an instance of WorkingClassA, and then call someFunction on this instance, with an instance of ParameterClassB:
let workingInstanceA = WorkingClassA()
workingInstanceA.someFunction(ParameterClassB()) // expects ParameterClassB
So far, nothing unusual. We passed a ParameterClassB instance to a function that expects a ParameterClassB.
Now let's assume that you swap your WorkingClassA instance with a WorkingClassB instance. This is perfectly legal in OOP – as the subclass can do everything the superclass could do.
let workingInstanceB = WorkingClassB()
workingInstanceB.someFunction(ParameterClassB()) // expects ParameterClassA
So what happens now? We're still passing a ParameterClassB instance into the function. However, now the function expects a ParameterClassA instance. Passing a subclass into an argument that expects a superclass is legal in OOP (this is covariance), as the subclass can do everything that the superclass could do – therefore this doesn't break anything.
Because the function signature can only get more broad (or remain unchanged) as you override it, it ensures that you can always pass it the original argument type that the function defined, as any superclass argument in an overridden version can accept it.
If you think about the reverse for a second, you'll see why it couldn't possibly work. As the function would get more restrictive as it gets overridden, it won't be able to accept arguments that the superclass could originally accept – therefore it cannot work.

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.

implicitely set an associated type

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).

Generic Constraints and Initializer Inheritance in Swift

I am trying to call an initializer required by protocol A on a type that both conforms to A and is a subclass of C.
All is fine and good if C is a base class. But as soon as C subclasses another class, say B, it breaks.
Here's what I am talking about:
protocol A {
init(foo: String)
}
class B {
init() {}
}
class C: B {}
func makeSomething<T: A>() -> T where T: B {
return T(foo: "Hi")
}
That works. But if I change where T: B to where T: C, I get the following error: argument passed to call that takes no arguments. It will only allow me to call Bs init.
Why can't I call this initializer? I understand that the super class has its own designated initializers that must be called. But that will be enforced when someone actually writes the class that subclasses B and conforms to A. (E.g. implementing init(Foo: String) and calling super.init(bar: Int) inside).
Any help here would be greatly appreciated. Thanks!
Swift provides a default initializer for your base class if it has all properties initialized but not incase of Generics because Swift may think its properties has not been initialized.
So when you constraint your return type as
func makeSomething<T: A>() -> T where T: C
It requires initialization for class C as Swift cannot provide a default initializer.But if your remove the where clause everything works fine
func makeSomething<T: A>() -> T {
return T(foo:"string")
}
If you want to return return T(foo: "Hi") :
You are getting error because class C doesn't have initializer that accepts init(foo: String)
Change your class C as
class C: A {
required init(foo: String) {
}
}
which provides at least one initializer that accepts the argument type.
So, remember if you don't subclass There is already one initializer doing your job and you dont get error.

swift subclasses in function types

Is there a way to assign a function with a parameter that is a subclass to a function variable with a parameter that is its superclass? Here is an example of what I mean:
class ClassA {}
class subclassOfA:ClassA {}
func subclassToNil(argument:subclassOfA) -> (){}
var functionVariable:(ClassA->())
funcVar = subclassToNil
This raises a type incompatibility exception.
I'm afraid not--you've discovered "covariance" and "contravariance". Function types are contravariant with their parameters (arguments), which means you could supply a superclass if you wanted, but not a subclass. With return values on the other hand, function types are are covariant and could return a subclass if you'd like.
With a little thought, these rules make sense:
class ClassA {}
class SubclassOfA: ClassA {}
func subclassToNil(argument: SubclassOfA) -> ()) {}
var functionVariable: (ClassA -> ())
functionVariable = subclassToNil
functionVariable(ClassA()) //`subclassToNil` won't know what to do with this; kablooie!
However:
class ClassParent {}
class ClassA: ClassParent {}
func subclassToNil(argument: ClassParent) -> ()) {}
var functionVariable:(ClassA -> ())
functionVariable = subclassToNil
functionVariable(ClassA()) //`ClassA()` is indeed a valid `ClassParent`, so we're fine.
So it's safe to use parameters that are less specific. The reasoning for return values is very similar, and you'll see that logically, you can use ones that are more specific.

Swift protocol and return types on global functions

This is a followup to the question: Protocol func returning Self. The protocol is as follows:
protocol Copyable {
init(copy: Self)
func copy() -> Self
}
The following works fine but the copy() function is exactly the same for every implementation, namely
func copy() -> Self {
return self.dynamicType(copy: self)
}
In accordance to this http://nshipster.com/swift-default-protocol-implementations/ I tried a global func
func copy<T : Copyable>(makeCopy: T) -> T {
return makeCopy.dynamicType(copy: makeCopy)
}
However, when it's called in a class implementing a the below protocol
protocol Mutatable : Copyable {
func mutated() -> Self
}
class C : Mutatable {
var a = 0
required init(_ a: Int) {
self.a = a
}
required init(copy: C) {
a = copy.a
}
func mutated() -> Self {
let mutated = copy(self)
mutated.a++
return mutated // error: 'C' is not convertible to 'Self'
}
}
I get the error as noted. When I type mutated autocomplete shows mutated as (C) and I have no idea what that means. I've also tried adding required to func mutated() but apparently required is only allowed for inits. Any way to get this to work?
This question has the same form as the copy one, and the same solution. Make mutation an initializer rather than a method.
protocol Copyable {
init(copy: Self)
}
protocol Mutatable : Copyable {
init(byMutating: Self)
}
class C : Mutatable {
var a = 0
required init(_ a: Int) {
self.a = a
}
required init(copy: C) {
a = copy.a
}
required convenience init(byMutating: C) {
self.init(copy: byMutating)
self.a++
}
}
// These are purely for convenience
func copy<T : Copyable>(x: T) -> T {
return x.dynamicType(copy: x)
}
func mutated<T: Mutatable>(x: T) -> T {
return x.dynamicType(byMutating: x)
}
But to reiterate Mattt's point in the linked article, you can have a C(copy: x) syntax fairly conveniently, and you can have a copy(x) syntax pretty conveniently, and there is always x.dynamicType(copy: x). But you can't have a x.copy() syntax without some annoying work. You either have to duplicate func copy() -> Self { return copy(self) } in every class, or you have to create some concrete class that implements this method and C ultimately inherits from. This is currently a basic limitation of Swift. I agree with Mattt's diagnosis of possible solutions, and suspect that some kind of trait system, possibly along the lines of Scala's, will probably be added in the future.
It's worth focusing on Mattt's comment that "all of this highlights a significant tension between methods and functions in Swift." This is another way of saying that there are tensions between the object-oriented paradigm and the functional paradigm, and moving between them can create some disconnects. Languages try to paper-over that issue with various features, but there are important differences between objects with messages and properties, vs functions with data and combinators, and "getting the best of both worlds" can sometimes create some rough edges.
It's easy to forget, when comparing Swift to other languages, that there is a big difference between v0.9 and v2.11. Many things we take for granted in our favorite languages did not exist in their v1 either.
To your comment, you may be thinking that mutated is of type Self. But it's of type C, as your autocomplete indicates. As before, C is not the same as Self unless you can promise that there are no subclasses (C being either final or a struct). Swift types are resolved at compile-time, not runtime, unless you use dynamicType.
To be a little more specific, Swift looks at this line:
let mutated = copy(self)
It notes that copy is generic on the type of its parameter, and it must construct a version of copy at compile-time to call. There is no type Self. It's just a placeholder, and must be resolved at compile-time. The type of self in this lexical scope is C. So it constructs copy<C>. But if you subclassed C, this could be the wrong function (and in this case, would be). This is very closely related to: https://stackoverflow.com/a/25549841/97337.
The fact that type autocomplete says (C) rather than C is a minor side-effect of how Swift functions and tuples work, and comes up pretty regularly, but I've yet to encounter a case where it really mattered. A Swift function like func f(x: Int, y:Int) does not actually have two parameters. It has one 2-tuple parameter of type (Int, Int). This fact is important to how the currying syntax works (see the Swift Programming Language for more on currying in Swift). So when you specialize copy, you specialized it with a 1-tuple of type (C). (Or possibly, the compiler is just trying to do that as one of various attempts, and that's just the one it reports on.) In Swift any value can be trivially exchanged for a 1-tuple of the same type. So the return of copy is actually the 1-tuple of C, written (C). I suspect that the Swift compiler will improve its messages over time to remove the extraneous parentheses, but that's why they show up sometimes.