0

I've read the In swift, why can't I instantiate a protocol when it has an initialiser?

My question is focused on why the compiler can't look into your default implementation and initialize an object based on that?

protocol ViewModel {
    var radius: Int { get }
    init()
}

extension ViewModel {
    var radius: Int { return 2}
    init() {}
}

let v = ViewModel() // ERROR: 

Protocol type 'ViewModel' cannot be instantiated

Question1:

Why doesn't Swift allow a vanilla initialization of a protocol? Why does it have to be tied to a concrete type?

I get it that it's not a concrete type. But why doesn't the compiler allow you to just create a type that is nothing but the protocol in its default value?! Is it because the compiler is like hey listen while I can either think of you as an interface/protocol or as an actual type. I can't think of you as both!? You're either something real in memory or just a blue-print.

If the language could have checked to see if an extension has provided implementation for all requirements then would it have made sense to allow initializing that as a special case? (I understand that it doesn't, but wondering is that the only thing needed to get this to work) Or even then this would have not made sense. If so why?

Additionally I tried do this instead:

protocol ViewModel {
    var radius: Int { get }
    init()
}

extension ViewModel {
    var radius: Int { return 2}
    init() {
        self.init() // Line A: 
    }
}

struct ReallyNothing: ViewModel {}

let v = ReallyNothing()

If I comment out LineA then I'll get an error of

'self.init' isn't called on all paths before returning from initializer

Question2:

WHY? why does the init have to call self.init() it seems a bit like a recursive loop.

mfaani
  • 33,269
  • 19
  • 164
  • 293
  • A protocol just defines an interface. It isn't concrete. – rmaddy Nov 11 '19 at 23:11
  • ``Why does it have to be tied to a concrete type?`` Coz it's what it designated for – Shehata Gamal Nov 11 '19 at 23:12
  • @rmaddy _A protocol just defines an interface. It isn't concrete._ So? – mfaani Nov 11 '19 at 23:13
  • 1
    Have a look at [In swift, why can't I instantiate a protocol when it has an initialiser?](https://stackoverflow.com/a/51018622/4667835). TL;DL: a protocol is not a concrete type, so when you would call `Protocol.init()`, the compiler wouldn't know what concrete type to initialise. It would only know that the type you want to initialise has the interface described by `Protocol`. – Dávid Pásztor Nov 11 '19 at 23:26
  • 1
    @DávidPásztor I saw it thanks. _the compiler wouldn't know what concrete type to initialise_ does it have to? It already has all the implementation detail it needs in its extension. How does a compiler not have to implement a defaulted function? How is this different? Why can't the compiler allow the default implementation as its most basic conformance? I guess you could argue that the compiler has no way of knowing whether or not you've implemented every requirement of it or not. Right? So relying on the extension is not a bullet-proof solution – mfaani Nov 11 '19 at 23:37

3 Answers3

2

A protocol is not a concrete type, so when you would call Protocol.init(), the compiler wouldn't know what concrete type to initialise. It would only know that the type you want to initialise has the interface described by Protocol.

The compiler has to know the concrete type because of the memory allocation. A protocol only defines a subset of required properties (and methods) that its conformant types must have, but doesn't define the whole set of properties its conformant types will have, since that actually differs type-by-type. Due to this, if you were to able to initialise a protocol type, the compiler and the runtime would have no idea about how much memory to allocate for that particular object, which is a piece of information without which no object can be initialised.

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Why can't the protocol be concretely initialized to that which is in the extension? – mfaani Nov 11 '19 at 23:46
  • 2
    @Honey it seems like you misunderstand the fact that something has an initialiser and that the runtime can actually create such object. Having an empty initialiser as a protocol requirement (or even a default implementation for it) only guarantees that any conformant types have such interface and that all of their stored properties have default values (or are initialised otherwise, without injection). However, as I've already stated, an empty init doesn't contain any information about the types and number of properties and hence about the memory requirements of the type. – Dávid Pásztor Nov 11 '19 at 23:50
  • 2
    Hence, the runtime cannot actually allocate such object. The empty init only gives you, the programmer a contract that any conformant types of that protocol will have an init that takes no input arguments. The default implementation for that init also allows you, the programmer, not to implement the empty init in conforming types. However, the runtime needs more information to be able to allocate memory for any objects, namely the number and type of all properties. This information isn't contained by the init at all. – Dávid Pásztor Nov 11 '19 at 23:52
  • Can you also address the 2nd question I asked in the original question? – mfaani Nov 11 '19 at 23:55
  • @Honey Sadly I'm sure about why that works, but it doesn't change the answer to the original question, since even with your 2nd example, the protocol type cannot be initialised. – Dávid Pásztor Nov 11 '19 at 23:59
  • I'm not sure I follow. The following compiles for me. ```protocol ViewModel { var radius: Int { get } init() } extension ViewModel { var radius: Int { return 2} init() { self.init() } } struct ReallyNothing: ViewModel {} let v = ReallyNothing() print(v.radius) // 2 ``` My 2nd was about recursion... – mfaani Nov 12 '19 at 00:02
  • `radius` is a computed property, so nothing is being stored per-instance. Objects have a pointer to their type (a so-called metatype object), and that metatype object has a table of all the various functions that can be executed upon instances of that type. That `2` lives within a function body, pointed to by the table of the metatype object for `ReallyNothing`. – Alexander Nov 12 '19 at 00:14
  • @Honey yeah, that does compile, but even with that init, `let v = ViewModel()` doesn't compile, which is what the original question was – Dávid Pásztor Nov 12 '19 at 00:16
  • @Alexander I don't understand why you brought that up. Can you explain? Where you saying that to oppose what Dávid said ie the concrete type needs to be known so it can allocate its memory? Could you also address [this comment](https://stackoverflow.com/questions/58809813/why-cant-you-initialize-a-protocol-that-has-default-implementation/58810259?noredirect=1#comment103900078_58810259) – mfaani Nov 12 '19 at 00:19
  • @Honey No I was saying it to you to show how your example isn't a demonstration of memory allocation in the protocl's default initializer. While every instance of a type conforming to `ViewModel` would have a `radius`, and its value would be `2`, that's not because of such instance has memory allocated to that `2`. – Alexander Nov 12 '19 at 00:22
  • @Alexander Aha! For the first time I finally understood what **metatype** means. So it's basically things shared by all conforming types of a protocol. All have access to the protocol requirements and its default implementation (which they can provide on their own as well) — it's not something that changes per instance. Cool! – mfaani Nov 12 '19 at 00:29
  • "an empty init doesn't contain any information about the types and number of properties and hence about the memory requirements of the type" many concrete objects have default params with empty `init` methods. I don't think what you mentioned is a good reason. Is there anything wrong with my own answer? – mfaani Nov 14 '19 at 14:36
  • It's about the fact that a protocol only defines a subset of the required interface, not the complete interface of the conforming objects. Conforming types can have more properties than required by the protocol, this is why you need concrete types for knowing the necessary memory requirements. And as I've stated several times previously, an init doesn't tell anything about the memory requirements for a type, since properties can have default values that are not allocate inside the init. – Dávid Pásztor Nov 14 '19 at 14:54
1

This has nothing to do with what whether ViewModel has an init. It has to do with what ViewModel is. It is a protocol.

A protocol is not an object. There is no such thing as an instance of a protocol. What would it mean to "instantiate" a protocol? Nothing. You can make an instance of:

  • an enum
  • a struct
  • or a class

A protocol is none of those. You cannot make an instance of a protocol.

ViewModel is a protocol. So you cannot make an instance of it. The phrase ViewModel() is meaningless. And the compiler tells you so.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    If the language could have checked to see if an extension has provided implementation for all requirements then would it have made sense to allow initializing that as a special case? (I understand that it doesn't, but wondering is that the only thing needed to get this to work) Or even then this would have not made sense. Because otherwise I have to create concrete types that do nothing but conforming and utilizing the default implementation. – mfaani Nov 12 '19 at 00:15
0

https://docs.swift.org/swift-book/LanguageGuide/Protocols.html

Protocols are blueprints and don't contain implementation- so you can't initialize them.

A class agrees to protocol and must provide the actual implementation.

Andy Stagg
  • 373
  • 5
  • 22
  • _Protocols are blueprints and don't contain implementation- so you can't initialize them_ In the `extension` I did provide implementation. – mfaani Nov 11 '19 at 23:13
  • I highly suggest you take a look at the link to the swift documentation. For starters, look at the way the protocol name differs from the class or struct name, and then notice that the class or struct name, opts into the protocol in it's definition. Extensions do just that, they extend an existing class (a practical example would be to have specific implementation for a given protocol in a separate file). You wouldn't ever extend a protocol itself. – Andy Stagg Nov 11 '19 at 23:19
  • @AndyStagg your last sentence is completely wrong. You often extend protocols when you want to provide a default implementation for specific methods of the protocol to share common functionality between different conforming types. – Dávid Pásztor Nov 11 '19 at 23:21
  • @DávidPásztor Sorry, my mistake- I haven't used them that way before, nor would have thought to. – Andy Stagg Nov 11 '19 at 23:22