1

This works

class Die {
    let faces: Int

    required init(faces: Int) {
        self.faces = faces
    }

    func yahtzeeDice() -> [Die] {
        return [Die](repeating: type(of: self).init(faces: 6), count: 5)
    }
}

This seems to violate DRY. It is possible to refer to Die indirectly in the yahtzeeDice function?

William Entriken
  • 37,208
  • 23
  • 149
  • 195
  • 1
    Are you talking about the `[Die]` in the method signature of `yahtzeeDice()` or the body? The latter could be replaced by `Array`, as the element type of `Die` can be inferred. – Hamish Jan 15 '17 at 17:25
  • 1
    Unrelated: Why is `yahtzeeDice()` an instance method? You could make it a `static`/`class` method and just use `self` rather than `type(of: self)`. – Hamish Jan 15 '17 at 17:28
  • You cannot use `[Self]` in the signature because array is a struct and if `A : B` then `[A]` and `[B]` are still unrelated types. – Sulthan Jan 15 '17 at 17:54
  • 2
    Also note that the repeating initializer of the `[Die]` array above with instantiate _a single_ `Die` instance, thereafter repeat the reference to this instance `5` times in the array (since `Die` is reference type). I.e., all members of `[Die]` hold a strong reference to the same underlying `Die` instance. – dfrib Jan 15 '17 at 18:33
  • What is the purpose of referring to it indirectly, i.e. what problem are you trying to solve? Do you plan to change the name of the class very often? – Andreas Jan 15 '17 at 18:46

1 Answers1

1

First of all, note that the repeating initializer of the [Die] array in your question will instantiate a single Die instance, thereafter repeat the reference to this instance 5 times in the array (since Die is reference type). I.e., all members of [Die] in your example hold a strong reference to the same underlying Die instance. So remember to avoid the repeating array initializer if you use reference types.


Now, you could construct a protocol that provides a default implementation of a blueprinted static dice supplier method, which supplies an array of Self instances, using some other blueprinted initializer.

// the 'class' requirement not strictly needed here, but it holds semantic
// value to explain the 'map' call rather than using array:s 'repeating' 
// initializer in in the default implementation below
protocol DiceFactory: class {
    init(faces: Int, id: Int)
    static func dice(_ n: Int) -> [Self]
}
extension DiceFactory {
    static func dice(_ n: Int) -> [Self] {
        return (1...n).map { Self.init(faces: 6, id: $0) }
    }
}

If you mark your Dice class final (why final? refer to the linked Q&A below), you'll have access to this default implementation directly when conforming to the DiceFactory protocol.

final class Die: DiceFactory {
    let id: Int
    let faces: Int

    init(faces: Int, id: Int) {
        self.faces = faces
        self.id = id
    }
}

let myDice = Die.dice(5)
myDice.forEach { print($0, $0.id) }
    /* Die 1
       Die 2
       Die 3
       Die 4
       Die 5 */

But the again, do you have a good reason not to explicitly type out Die?

See also:

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • One minor improvement, you can use `(1...n).map { Self.init(...) }` to generate the array. – Sulthan Jan 15 '17 at 19:04
  • 1
    Note that the use of `Array(repeating:count:)` *may* be intentional, given that `Die` instances are immutable (it's possible OP is just using a class for inheritance – if this isn't the case, then it should definitely be a `struct` instead). – Hamish Jan 15 '17 at 19:26