I'm running into a few oddities while trying to conform to StringLiteralConvertible:
class Person: StringLiteralConvertible {
var name = ""
init(name n:String){
name = n
}
init(stringLiteral value: StringLiteralType){
name = n
}
init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
name = n
}
init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
name = n
}
}
var ironMan = Person(name: "Tony Stark")
var spiderMan: Person = "Peter Parker"
I implemented both the ExtendedGraphemeClusterLiteralConvertible and the UnicodeScalarLiteralConvertible protocols (whatever on Earth that means).
However I still got errors and had to provide definitions for both ExtendedGraphemeClusterLiteralType and UnicodeScalarLiteralType:
typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String
Why did I have to provide this, if it's already in the standard header???
The compiler considered it still had the right to complain and forced me to add the required keyword to the inits, even though the definition of the protocols does not include the required keyword! Why????
The following code compiles, but I don't understand why the first version didn't compile!
class Person: StringLiteralConvertible {
var name = ""
init(name n:String){
name = n
}
typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String
required convenience init(stringLiteral value: StringLiteralType){
self.init(name: value)
}
required convenience init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
self.init(name: value)
}
required convenience init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
self.init(name: value)
}
}
Swift, if you like the compiler to yell at you, you're gonna love it! :-P
Why typealiases:
The answer to your first question lies in the fact that Swift protocols that have associated types can be considered abstract, and need to be bound to a specific type. Associated types are an alternative to using a more specialized approach like class MyClass : IsCompatibleWith<Int> {}. I think the reason for this is to have common hierarchies, but this limitation also causes many other headaches. No matter what though, it is reasonable and expected that you need to specify the type to which the protocol is bound. In Swift, you do this by type-aliasing associated type to the one you want (String in your case).
Also note, that the associated type is not specified in the protocol, the protocol only specifies that you must define it. That said, unless you have conflicting methods on your class, the compiler can generally infer the associated type (read this in the docs somewhere).
Required initializers:
It appears that initializers specified in protocols are required by default / nature. The documentation says that you can implement them as designnated or convenience, but that either way they are required:
Class Implementations of Protocol Initializer Requirements
You can implement a protocol initializer requirement on a conforming
class as either a designated initializer or a convenience initializer.
In both cases, you must mark the initializer implementation with the
required modifier:
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
The use of the required modifier ensures that you provide an explicit
or inherited implementation of the initializer requirement on all
subclasses of the conforming class, such that they also conform to the
protocol.
From my reading though, it answers what but not why.
Related
Swift noob here. Consider this Swift 5.7 code:
import Foundation
// This is according to the grammar.
protocol TestProtocol1 {
associatedtype T
associatedtype U
}
// Not allowed by the grammer, but still compiles.
protocol TestProtocol2<T> {
associatedtype T
associatedtype U
}
// Doesn't seem to matter if I add one or both type arguments.
protocol TestProtocol3<T, U> {
associatedtype T
associatedtype U
}
// This is fine. As expected.
class TestClass1 : TestProtocol1 {
typealias T = Int
typealias U = Bool
}
// Fine too. Even though I don't specify the type arguments.
class TestClass2 : TestProtocol2 {
typealias T = Int
typealias U = Bool
}
// error: cannot inherit from protocol type with generic argument 'TestProtocol3<Int, Bool>'
class TestClass3 : TestProtocol3<Int, Bool> {
typealias T = Int
typealias U = Bool
}
Questions:
Is there any semantic difference between the three protocol definitions?
Why does it compile when declaring the associated types as generic arguments when the grammar doesn't allow it?
Why is it useful to (probably redundantly) add type arguments to protocols? For
example, protocol Sequence<Element> does this too.
The "generic parameters" that you are seeing are the protocols' primary associated types, proposed in SE-0346, implemented in Swift 5.7, I suppose the grammar section in the language reference just hasn't been updated yet.
The three protocol declarations are semantically different, in that they have different primary associated types. When using the protocol in certain positions, primary associated types are what you can directly specify in <...>, rather than specify them somewhere else like in a where clause. For example, when using the protocol as a generic constraint:
func foo<P: TestProtocol2<Int>>(p: P) { ... }
is syntactic sugar for:
func foo<P: TestProtocol2>(p: P) where P.T == Int { ... }
The former is just a little more concise :)
You cannot do something similar with TestProtocol1, because it doesn't have primary associated types.
For TestProtocol3, you must specify both primary associated types:
func foo<P: TestProtocol3<Int, Bool>>(p: P) { ... }
According to the SE proposal, this syntax was also planned to be usable in the protocol conformance clause of a concrete type, like in your code:
class TestClass3 : TestProtocol3<Int, Bool> { // does not compile
However, this feature did not get added for some reason. You can still use it in the inheritance clause of a protocol though:
protocol TestProtocol4: TestProtocol3<Int, Bool> { } // works
See the SE proposal for more details.
For the first question Is there any semantic difference between the three protocol definitions?
I dont think so . When you create a protocol with <..> after protocol name , Protocols think that the name giving between <> is a associated type or types but you must add that associatedtype name with the same in <>
For example if you delete U TestProtocol3 like
protocol TestProtocol3<T, U>{
associatedtype T
}
you will get an error says : An associated type named 'U' must be declared in the protocol 'TestProtocol3' or a protocol it inherits.In the opposite way if you delete U in protocol TestProtocol3<T, U> like protocol TestProtocol3<T> , will not give an any error.
Here is my answer to your questions:
Is there any semantic difference between the three protocol definitions?
The only difference it's that TestProtocol1 has the correct declaration.
Why does it compile when declaring the associated types as generic arguments when the grammar doesn't allow it?
I made the test and it doesn't compile for me! kind weird 🤔
Why is it useful to (probably redundantly) add type arguments to protocols? For example, protocol Sequence does this too.
I'm not sure that I understand your question here but, the Sequence protocol is declared in the same way you had declared your TestProtocol1
/// A sequence should provide its iterator in O(1). The `Sequence` protocol
/// makes no other requirements about element access, so routines that
/// traverse a sequence should be considered O(*n*) unless documented
/// otherwise.
public protocol Sequence {
/// A type representing the sequence's elements.
associatedtype Element where Self.Element == Self.Iterator.Element
/// A type that provides the sequence's iteration interface and
/// encapsulates its iteration state.
associatedtype Iterator : IteratorProtocol
...
}
``
I have a protocol with an optional property.
Most of the types that conform to this protocol will have a matching optional property. However, one has a non-optional property of the same type and name.
protocol SomeProtocol {
var foo: Int? { get }
}
struct StructA: SomeProtocol {
let foo: Int?
}
struct StructB: SomeProtocol {
let foo: Int // Type 'StructB' does not conform to protocol 'SomeProtocol'
}
Pressing Xcode's "Fix - Do you want to add protocol stubs?" button adds the optional version of the property, but the structure now has invalid duplicated variable names:
struct StructB: SomeProtocol {
let foo: Int
var foo: Int? { return foo } // Invalid redeclaration of 'foo'
}
In the { get }-only case, I had assumed that this would "just work" due to the non-optional always satisfying the constraints of the optional, similar to how you can return a non-optional in a function with an optional return type. But apparently that is not the case.
This works the same for functions as well; a protocol's func bar() -> Int? is not satisfied by a conforming type declaring func bar() -> Int.
Is there any way around this issue? I would prefer not to rename the variables or add intermediate getters.
Has this situation been considered for Swift? What is the rational for not allowing a non-optional to satisfy an optional protocol variable?
If the protocol provides a default implementation that returns an optional:
protocol SomeProtocol {
var foo: Int? { get }
}
extension SomeProtocol {
var foo: Int? { return nil }
}
protocol-conforming types can then provide an overriding non-optional version of the variable/function:
struct StructB: SomeProtocol {
let foo: Int
}
I found this discussed on the Swift Evolution forum:
At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.
https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448
This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:
Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)
Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns
which is tracked on Stack Overflow here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
The extension-with-default-implementation solution offered by pkamb is helpful in terms of getting the compiler to recognize conformance, but you need to be aware of the strange behavior this can produce.
An object of the newly conforming type will return a different value depending on what type you cast it to (this also includes passing as to a parameter whose type is the protocol):
let bar = StructB(foo: 7)
let baz: SomeProtocol = bar
bar.foo // evaluates to 7
baz.foo // evaluates to nil (surprise!)
As someone recently commented on a related Swift bug ticket: "This can be quite surprising and perhaps could be considered a bug of its own?"
It definitely tripped me up.
I am trying to create a new instance of a codable class, but I am not sure how to:
var x = Articles();
gives me the following error:
Missing argument for parameter 'from' in call
class Articles: Codable {
let value: [Article]?
}
I don't understand since this is a parameterless class. I have no idea what the from parameter is all about.
I don't understand since this is a parameterless class. I have no idea
what the from parameter is all about.
I get no error when I run the following:
class Articles: Codable {
let value: [Articles]?
init() {
value = nil
print("in init()")
}
}
var x = Articles()
output:
in init()
And without init():
class Articles: Codable {
let value: [Articles]?
// init() {
// value = nil
// print("in init()")
// }
}
var x = Articles() //Missing argument for parameter 'from' in call
First, read this:
Automatic Initializer Inheritance
As mentioned above, subclasses do not inherit their superclass
initializers by default. However, superclass initializers are
automatically inherited if certain conditions are met. In practice,
this means that you do not need to write initializer overrides in many
common scenarios, and can inherit your superclass initializers with
minimal effort whenever it is safe to do so.
Assuming that you provide default values for any new properties you
introduce in a subclass, the following two rules apply:
Rule 1 If your subclass doesn’t define any designated initializers, it
automatically inherits all of its superclass designated initializers.
If you look at the docs, Codable is a typealias for the Decodable protocol (a protocol is like an interface in java). Protocols specify functions that a class must implement if they adopt the protocol. Your Articles class adopts the Decodable protocol. However, the docs for Decodable say,
init(from: Decoder)
Creates a new instance by decoding from the given decoder. Required.
Default implementation provided.
Protocols can actually implement functions by using extensions, which are then inherited by the class adopting the protocol.
As a result, the only designated initializer for your class is the one defined in the Decodable protocol, which takes one argument--which you inherit according to Rule 1. On the other hand, when you explicitly define another designated initializer in your class that takes no arguments, then you will call that initializer when you provide no arguments.
In the following code, I want to test if x is a SpecialController. If it is, I want to get the currentValue as a SpecialValue. How do you do this? If not with a cast, then some other technique.
The last line there won't compile. There error is: Protocol "SpecialController" can only be used as a generic constraint because it has Self or associated type requirements.
protocol SpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController { // does not compile
Unfortunately, Swift doesn't currently support the use of protocols with associated types as actual types. This however is technically possible for the compiler to do; and it may well be implemented in a future version of the language.
A simple solution in your case is to define a 'shadow protocol' that SpecialController derives from, and allows you to access currentValue through a protocol requirement that type erases it:
// This assumes SpecialValue doesn't have associated types – if it does, you can
// repeat the same logic by adding TypeErasedSpecialValue, and then using that.
protocol SpecialValue {
// ...
}
protocol TypeErasedSpecialController {
var typeErasedCurrentValue: SpecialValue? { get }
}
protocol SpecialController : TypeErasedSpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
extension SpecialController {
var typeErasedCurrentValue: SpecialValue? { return currentValue }
}
extension String : SpecialValue {}
struct S : SpecialController {
var currentValue: String?
}
var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController {
print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
}
[Edited to fix: : SpecialValue, not = SpecialValue]
This is not possible. SpecialValueController is an "incomplete type" conceptually so the compiler cannot know. SpecialValueType, although it is constrained by SpecialValue, it is not known until it is determined by any adopting class. So it is a really placeholder with inadequate information. as?-ness cannot be checked.
You could have a base class that adopts SpecialController with a concrete type for SpecialValueController, and have multiple child classes that inherit from the adopting class, if you're still seeking a degree of polymorphism.
This doesn't work because SpecialController isn't a single type. You can think of associated types as a kind of generics. A SpecialController with its SpecialValueType being an Int is a completely different type from a SpecialController with its SpecialValueType being an String, just like how Optional<Int> is a completely different type from Optional<String>.
Because of this, it doesn't make any sense to cast to SpecialValueType, because that would gloss over the associated type, and allow you to use (for example) a SpecialController with its SpecialValueType being an Int where a SpecialController with its SpecialValueType being a String is expected.
As compiler suggests, the only way SpecialController can be used is as a generic constraint. You can have a function that's generic over T, with the constraint that T must be a SpecialController. The domain of T now spans all the various concrete types of SpecialController, such as one with an Int associated type, and one with a String. For each possible associated type, there's a distinct SpecialController, and by extension, a distinct T.
To draw out the Optional<T> analogy further. Imagine if what you're trying to do was possible. It would be much like this:
func funcThatExpectsIntOptional(_: Int?) {}
let x: Optional<String> = "An optional string"
// Without its generic type parameter, this is an incomplete type. suppose this were valid
let y = x as! Optional
funcThatExpectsIntOptional(y) // boom.
I have this kind of code:
protocol MyProtocol {
}
class P1: MyProtocol {
}
class P2: MyProtocol {
}
class C <T: MyProtocol> {
}
Then i need to define a variable to delegate all kinds of C<MyProtocol>:
var obj: C <MyProtocol>
But compile error comes:
Using 'MyProtocol' as a concrete type conforming to protocol 'MyProtocol' is not supported
How can I do?
This code:
class C <T: MyProtocol> { }
Means that C is a generic class that can specialize on any type T that conforms to MyProtocol.
When you declare:
var obj: C <MyProtocol>
You are (I think) trying to say that the var obj will be an instance of C specialized to some type that conforms to MyProtocol,but you can't specialize a generic class on a protocol type, because there is no such thing as a direct concrete instance of a protocol. There can only be instances of a type conforming to the protocol. And there can theoretically be many different types that conform to the protocol. So that notation doesn't really tell the compiler which specific specialization of C to use.
This shouldn't be a problem though, because you can write things like:
var obj: C<P1> = C<P1>()
or
var obj = C<P2>() // type is inferred
And within your class C you can still treat any uses of T as conforming to MyProtocol. So I think this should give you everything you need, as long as you remember that an instance of a generic class must be specialized to a single specific concrete type, not a protocol which could represent many possible concrete types.
You usually don't need to declare type for a Swift's variable. The compiler can infer type in most cases. Also, for generic class, you should let the compiler figure out what the generic classes resolve to:
class C<T: MyProtocol> {
var value: T
init (value: T) {
self.value = value
}
}
var obj = C(value: P1()) // type: C<P1>
// or:
var obj = C(value: P2()) // type: C<P2>