0

I'm working in Swift 4.

I want to define a Problem.

A Problem consists of a Question and an Answer.

The Question might be any of: String, Int, [Int], Image, [Image] or some new Type not defined or could be some combination of the above.

The Answer might also be any of the above but a given Question can have an Answer of a different type.

(e.g. question = (Image, "What type of animal is this?), answer = ("A Cat") )

So I thought protocols would be the way to go:

protocol Posable {
  var pose: String { get }
}

protocol Answerable: Hashable {
  var answer: String { get }
}

protocol Puzzle {
  var problem: Posable { get }
  var solution: Answerable { get }
}

I made Answerable Hashable because I want to be able to compare Answers and create sets of Answers.

But I get on the solution: Answerable line:

'Protocol 'Answerable' can only be used as a generic constraint because it has Self or associated type requirements.

I understand why that is but...

Can anyone make any suggestions about how to implement this so that I don't run into that problem?

I'm keen to implement with protocols if possible, partly so that I learn about them.

Cortado-J
  • 2,035
  • 2
  • 20
  • 32
  • Possible duplicate of [Protocol can only be used as a generic constraint because it has Self or associatedType requirements](https://stackoverflow.com/questions/36348061/protocol-can-only-be-used-as-a-generic-constraint-because-it-has-self-or-associa) – Papershine Jul 04 '18 at 12:51

2 Answers2

1

I would solve it with generics :

import UIKit

struct Posable<T> {
    var pose: T
}

struct Answerable<T> {
    var answer: T
}

extension Answerable: Equatable where T: Equatable {
    static func ==(lhs: Answerable<T>, rhs: Answerable<T>) -> Bool {
        return lhs.answer == rhs.answer
    }
}

extension Answerable: Hashable where T: Hashable {
    var hashValue: Int {
        return answer.hashValue
    }
}

struct Puzzle<T, U> {
    var problem: Posable<T>
    var solution: Answerable<U>
}

let image = UIImage() // Image of a cat
let pose = Posable<(UIImage, String)>(pose: (image, "What type of animal is this?"))
let solution = Answerable<String>(answer: "A cat")

let myPuzzle = Puzzle<(UIImage, String), String>(problem: pose, solution: solution)

Generics allow you to make very reusable code! Here you can use any class as a question or as an anwser.

With Swift type inherence : you also get to simplify the initialization :

let image = UIImage() // Image of a cat
let pose = Posable(pose: (image, "What type of animal is this?"))
let solution = Answerable(answer: "A cat")

let myPuzzle = Puzzle(problem: pose, solution: solution)
Dean
  • 1,512
  • 13
  • 28
  • Hi Dean, Your reply is totally awesome. It does exactly what I want. I've been wondering whether Generics might help but haven't used them much. Thanks very much. I will explore. Thanks so much. – Cortado-J Jul 04 '18 at 19:11
  • 1
    And swift 4.1 beings synthesized conformance for Equatable and Hasble so we can just say: extension Answerable: Equatable where T: Equatable { } and extension Answerable: Hashable where T: Hashable { } – Cortado-J Jul 14 '18 at 10:52
1

I'm not sure but you can remove Hashable from Answerable.

protocol Posable {
    var pose: String { get }
}

protocol Answerable {
    var answer: String { get }
}

protocol Puzzle {
    var problem: Posable? { get }
    func getSolution<T: Hashable & Answerable>() -> T
}
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194