File: InitializerProblems.rst

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (106 lines) | stat: -rw-r--r-- 4,141 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
:orphan:

.. title:: Problems with Initializers


Problem 1: Initializers are complicated
=======================================

By formalizing Objective-C's initialization conventions, we've ended up with a
tower of complexity where users find it easier to do the wrong thing and then
follow the compiler fix-its. I [Jordan R] still feel like the individual rules
aren't so complicated:

    | Designated initializers chain.
    | Designated initializers are inherited if (a) there are no manual
      initializers, and (b) all properties have initial values.

    | Convenience initializers delegate.
    | Convenience initializers are inherited if all of the superclass's
      designated initializers are present.

    | If you want to call an initializer on a dynamic type, it must be marked
      required.
    | Protocols are one way to do this, so initializers that satisfy protocol
      requirements must be required.
    | If your superclass has a required initializer, you must provide it
      somehow.

but

    "When even Andy Matuschak and Rob Rix can't understand your model, you have
    a problem." - Joe Groff


Problem 2: Convenience initializers are missing use cases
=========================================================

With all our rules, we actually rule out some important use cases, like this one on NSDocument:

    The ``init`` method of NSDocument is the *designated initializer,* and it is
    invoked by the other initializers ``initWithType:error:`` and
    ``initWithContentsOfURL:ofType:error:``. If you perform initializations that
    must be done when creating new documents but not when opening existing
    documents, override ``initWithType:error:``. If you have any initializations
    that apply only to documents that are opened, override
    ``initWithContentsOfURL:ofType:error:``. If you have general
    initializations, override ``init``. In all three cases, be sure to invoke
    the superclass implementation as the first action.

    -- `Document-Based App Programming Guide for Mac`__

__ https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/DocBasedAppProgrammingGuideForOSX/ManagingLifecycle/ManagingLifecycle.html#//apple_ref/doc/uid/TP40011179-CH4-SW11

Because we don't allow overriding convenience initializers with other
convenience initializers, there's nowhere to perform post-customization of
NSDocuments for each particular case.


Problem 3: Factory Initializers
===============================

Finally, we try to standardize on initializers for object creation in Swift,
even going as far as to import Objective-C factory methods as initializers...but
there are some patterns that cannot be written in Swift, such as this one::

    class AnyGenerator<Element> : GeneratorType {
      init<
        WrappedGenerator: GeneratorType
      where
        WrappedGenerator.Element == Element
      >(wrapped: WrappedGenerator) -> AnyGenerator {
        return AnyGeneratorImpl(wrapped)
      }
      // other generator stuff
    }

    class AnyGeneratorImpl<WrappedGenerator: GeneratorType> :
        AnyGenerator<WrappedGenerator.Element> {
      var wrapped: WrappedGenerator
      init(wrapped: WrappedGenerator) {
        self.wrapped = wrapped
      }
      // other generator stuff
    }

We ended up making ``AnyGenerator`` a struct that wraps ``AnyGeneratorImpl`` to
get around this, but it's not a nice solution.


Solutions?
==========

We've had a number of ideas for improving the state of the world, including

- Allow designated initializers to delegate to other designated initializers
  (using static dispatch). This makes convenience initializers a niche feature.

- Add the concept of factory initializers, which don't promise to return
  ``Self``. These are either never inherited or must always be overridden in a
  subclass.

- Allow convenience initializers to chain to superclass convenience
  initializers. This isn't strictly safe, but it permits the NSDocument idiom.

None of these solve all the initializer problems listed above on their own, and
we'd want to be careful not to *increase* complexity in this space.