6

I understand that generally I cannot instantiate a protocol. But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use? My code is as below and line:

protocol Solution {
  var answer: String { get }
}

protocol Problem {
  var pose: String { get }
}

protocol SolvableProblem: Problem {
  func solve() -> Solution?
}

protocol ProblemGenerator {
  func next() -> SolvableProblem
}

protocol Puzzle {
  var problem: Problem { get }
  var solution: Solution { get }

  init(problem: Problem, solution: Solution)
}

protocol PuzzleGenerator {
  func next() -> Puzzle
}

protocol FindBySolvePuzzleGenerator: PuzzleGenerator {
  var problemGenerator: ProblemGenerator { get }
}

extension FindBySolvePuzzleGenerator {
  func next() -> Puzzle {
    while true {
      let problem = problemGenerator.next()
      if let solution = problem.solve() {
        return Puzzle(problem: problem, solution: solution)
      }
    }
  }
}

The line:

return Puzzle(problem: problem, solution: solution)

gives error: Protocol type 'Puzzle' cannot be instantiated

Cortado-J
  • 2,035
  • 2
  • 20
  • 32
  • And if there isn;t a way round the issue of instantiating, then is there another way for me to return a Puzzle from the protocol extension? – Cortado-J Jun 25 '18 at 07:35
  • "But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later", Nope; Possibly Related: https://stackoverflow.com/questions/41074354/protocol-type-cannot-be-instantiated – Ahmad F Jun 25 '18 at 07:36

5 Answers5

8

Imagine protocols are adjectives. Movable says you can move it, Red says it has color = "red"... but they don't say what it is. You need a noun. A Red, Movable Car. You can instantiate a Car, even when low on details. You cannot instantiate a Red.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Thanks, I get that. So if that means I can't get my FindBySolvePuzzleGenerator to instantiate a Puzzle, is there any other way to implement what I am describing? I know that I will be implementing FindBySolvePuzzleGenerator and that it will generate Puzzles. I also know how it will do that and that's what I am trying to describe in the extension but can't see how to do that. – Cortado-J Jun 25 '18 at 07:50
  • Why do you think a `Puzzle` needs to be a protocol in the first place? (or indeed most of the entities you created). Make sure you understand the `is-a` and `has-a` relationships as well, and make sure you name things accordingly. To give one example, I'd expect `solution` to be a field in a class `Puzzle`; possibly a field in a protocol named `Solvable` or `HasSolution` or something. `Solution` should not be a protocol. `question` should absolutely be directly a member of `Puzzle` class, because it is not a puzzle if it doesn't pose a question. Making it into a protocol makes no sense. – Amadan Jun 25 '18 at 07:50
  • Thanks @Amadan, useful questions. My thinking was that my puzzles can consist of very different problems and solutions. Some problems might be a string of text, some might be an image, answers similarly can be variable - so I was trying to stay as generic as possible to allow for future variations. But... and this is why your question is useful - even though my problem and my solution may vary, a Puzzle (at least as I'm thinking of it here), always consists of a Problem and a Solution and so can be a struct rather than a protocol - I'll try it... and ponder on your other comments. – Cortado-J Jun 25 '18 at 07:56
  • If you have different questions, make an abstract class `Question`, subclass it with different kinds of question. Again, not a matter of protocol. Question classes that share certain types (e.g. `Drawable`, `SelectableFromAList`) might be good protocols to implement if it makes sense. (You might want to include `answer` in those subclasses as well, as different types of questions have different kinds of answers.) – Amadan Jun 25 '18 at 07:57
  • By changing Puzzle from a protocol to a struct the problem goes away. I think I had got a bit trigger happy with changing everything into protocols. I will carefully check, but I think all my other types need to stay as protocols. Thanks again @Amadan for your help. – Cortado-J Jun 26 '18 at 19:15
3

I understand that I can't do it - I just want to understand why the compiler can't do it?

Because protocols in Swift represent abstraction mechanism. When it comes to abstraction, you could think about it as a template, we don't have to care about the details of how it behaves or what's its properties; Thus it makes no sense to be able to create an object from it.

As a real world example, consider that I just said "Table" (as an abstracted level), I would be pretty sure that you would understand what I am talking about! nevertheless we are not mentioning details about it (such as its material or how many legs it has...); At some point if I said "create a table for me" (instantiate an object) you have the ask me about specs! and that's why the complier won't let you create object directly from a protocol. That's the point of making things to be abstracted.

Also, checking: Why can't an object of abstract class be created? might be helpful.

mfaani
  • 33,269
  • 19
  • 164
  • 293
Ahmad F
  • 30,560
  • 17
  • 97
  • 143
3

But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use?

Protocols must be adopted by classes, and there might be a dozen different classes that all adopt your Puzzle protocol. The compiler has no idea which of those classes to instantiate.

Protocols give us the power to compose interfaces without the complexity of multiple inheritance. In a multiple inheritance language like C++, you have to deal with the fact that a single class D might inherit from two other classes, B and C, and those two classes might happen to have methods or instance variables with the same name. If they both have a methodA(), and B::methodA() and C::methodA() are different, which one do you use when someone call's D's inherited methodA()? Worse, what if B and C are both derived from a common base class A? Protocols avoid a lot of that by not being directly instantiable, while still providing the interface polymorphism that makes multiple inheritance attractive.

Caleb
  • 124,013
  • 19
  • 183
  • 272
2

When you instantiate an object, the Operating System has to know how to allocate and deal with that kind of object in the memory: Is it a reference type (Classes)? Strong, weak or unowned reference? Or is it a value type (Structs, Strings, Int, etc)?

Reference types are stored in the Heap, while value types live in the Stack. Here is a thorough explanation of the difference between the two.

Only Reference and Value types (objects) can be instantiated. So, only the objects that conform to that protocol can then be instantiated, not the protocol itself. A protocol is not an object, it is a general description or schema of a certain behavior of objects.

As to Initialization, here what the Apple docs say:

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

ielyamani
  • 17,807
  • 10
  • 55
  • 90
1

Unfortunately swift does not allow that even with such "hack"

You would need to use a class that confirms to that protocol as an object you refer to.

Taier
  • 2,109
  • 12
  • 22