File: valref.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 (577 lines) | stat: -rw-r--r-- 21,587 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
:orphan:

.. _valref:

=======================
 Values and References
=======================

:Author: Dave Abrahams
:Author: Joe Groff
:Date: 2013-03-15

**Abstract:** We propose a system that offers first-class support for
both value and reference semantics.  By allowing--but not
requiring--(instance) variables, function parameters, and generic
constraints to be declared as ``val`` or ``ref``, we offer users the
ability to nail down semantics to the desired degree without
compromising ease of use.

.. Note::

   We are aware of some issues with naming of these new keywords; to
   avoid chaos we discuss alternative spelling schemes in a Bikeshed_
   section at the end of this document.

Introduction
============

Until recently, Swift's support for value semantics outside trivial
types like scalars and immutable strings has been weak.  While the
recent ``Clonable`` proposal makes new things possible in the "safe"
zone, it leaves the language syntactically and semantically lumpy,
keeping interactions between value and reference types firmly outside
the "easy" zone and failing to address the issue of generic
programming.

This proposal builds on the ``Clonable`` proposal to create a more
uniform, flexible, and interoperable type system while solving the
generic programming problem and expanding the "easy" zone.

General Description
===================

The general rule we propose is that most places where you can write
``var`` in today's swift, and also on function parameters, you can
write ``val`` or ``ref`` to request value or reference semantics,
respectively.  Writing ``var`` requests the default semantics for a
given type.  Non-``class`` types (``struct``\ s, tuples, arrays,
``union``\ s) default to ``val`` semantics, while ``class``\ es
default to ``ref`` semantics. The types ``val SomeClass`` and
``ref SomeStruct`` also become part of the type system and can
be used as generic parameters or as parts of tuple, array, and
function types.

Because the current specification already describes the default
behaviors, we will restrict ourselves to discussing the new
combinations, such as ``struct`` variables declared with ``ref`` and
``class`` variables declared with ``val``, and interactions between
the two.

Terminology
===========

When we use the term "copy" for non-``class`` types, we are talking
about what traditionally happens on assignment and pass-by-value.
When applied to ``class`` types, "copy" means to call the ``clone()``
method, which is generated by the compiler when the user has
explicitly declared conformance to the ``Clonable`` protocol.

When we refer to variables being "declared ``val``" or "declared
``ref``", we mean to include the case of equivalent declarations using
``var`` that request the default semantics for the type.

Unless otherwise specified, we discuss implementation details such as
"allocated on the heap" as a way of describing operational semantics,
with the understanding that semantics-preserving optimizations are
always allowed.

When we refer to the "value" of a class, we mean the combination of
values of its ``val`` instance variables and the identities of its
``ref`` instance variables.

Variables
=========

Variables can be explicitly declared ``val`` or ``ref``::

    var x: Int  // x is stored by value
    val y: Int  // just like "var y: Int"
    ref z: Int  // z is allocated on the heap.

    var q: SomeClass          // a reference to SomeClass
    ref r: SomeClass          // just like "var r: SomeClass"
    val s: SomeClonableClass // a unique value of SomeClonableClass type

Assignments and initializations involving at least one ``val`` result
in a copy.  Creating a ``ref`` from a ``val`` copies into heap memory::

    ref z2 = x         // z2 is a copy of x's value on the heap
    y = z              // z2's value is copied into y

    ref z2 = z         // z and z2 refer to the same Int value
    ref z3 = z.clone() // z3 refers to a copy of z's value

    val t = r          // Illegal unless SomeClass is Clonable
    ref u = s          // s's value is copied into u
    val v = s          // s's value is copied into v

Standalone Types
================

``val``\ - or ``ref``\ -ness is part of the type.  When the type
appears without a variable name, it can be written this way::

   ref Int                 // an Int on the heap
   val SomeClonableClass  // a value of SomeClonableClass type

Therefore, although it is not recommended style, we can also write::

    var y: val Int               // just like "var y: Int"
    var z: ref Int               // z is allocated on the heap.
    var s: val SomeClonableClass // a unique value of type SomeClonableClass

Instance Variables
==================

Instance variables can be explicitly declared ``val`` or ``ref``::

  struct Foo {
      var x: Int  // x is stored by-value
      val y: Int  // just like "var y: Int"
      ref z: Int  // allocate z on the heap

      var q: SomeClass          // q is a reference to SomeClass
      ref r: SomeClass          // just like "var r: SomeClass"
      val s: SomeClonableClass // clone() s when Foo is copied
  }

  class Bar : Clonable {
      var x: Int  // x is stored by-value
      val y: Int  // just like "var y: Int"
      ref z: Int  // allocate z on the heap

      var q: SomeClass          // q is stored by-reference
      ref r: SomeClass          // just like "var r: SomeClass"
      val s: SomeClonableClass // clone() s when Bar is clone()d
  }

When a value is copied, all of its instance variables declared ``val``
(implicitly or explicitly) are copied.  Instance variables declared
``ref`` merely have their reference counts incremented (i.e. the
reference is copied).  Therefore, when the defaults are in play, the
semantic rules already defined for Swift are preserved.

The new rules are as follows:

* A non-``class`` instance variable declared ``ref`` is allocated on
  the heap and can outlive its enclosing ``struct``.

* A ``class`` instance variable declared ``val`` will be copied when
  its enclosing ``struct`` or ``class`` is copied.  We discuss below__
  what to do when the ``class`` is not ``Clonable``.

Arrays
======

TODO: reconsider sugared array syntax.  Maybe val<Int>[42] would be better

Array elements can be explicitly declared ``val`` or ``ref``::

  var x : Int[42]         // an array of 42 integers
  var y : Int[val 42]     // an array of 42 integers
  var z : Int[ref 42]     // an array of 42 integers-on-the-heap
  var z : Int[ref 2][42]  // an array of 2 references to arrays
  ref a : Int[42]         // a reference to an array of 42 integers

When a reference to an array appears without a variable name, it can
be written using the `usual syntax`__::

  var f : () -> ref Int[42] // a closure returning a reference to an array
  var b : ref Int[42]       // equivalent to "ref b : Int[42]"

__ `standalone types`_

Presumably there is also some fully-desugared syntax using angle
brackets, that most users will never touch, e.g.::

  var x : Array<Int,42>               // an array of 42 integers
  var y : Array<val Int,42>           // an array of 42 integers
  var z : Array<ref Int,42>           // an array of 42 integers-on-the-heap
  var z : Array<ref Array<Int,42>, 2> // an array of 2 references to arrays
  ref a : Array<Int,42>               // a reference to an array of 42 integers
  var f : () -> ref Array<Int,42>     // a closure returning a reference to an array
  var b : ref Array<Int,42>           // equivalent to "ref b : Int[42]"

Rules for copying array elements follow those of instance variables.

``union``\ s
============

Union types, like structs, have default value semantics. Constructors for the
union can declare the ``val``- or ``ref``-ness of their associated values, using
the same syntax as function parameters, described below::

  union Foo {
    case Bar(ref bar:Int)
    case Bas(val bas:SomeClass)
  }

Unions allow the definition of recursive types. A constructor for a union
may recursively reference the union as a member; the necessary
indirection and heap allocation of the recursive data structure is implicit
and has value semantics::

  // A list with value semantics--copying the list recursively copies the
  // entire list
  union List<T> {
    case Nil()
    case Cons(car:T, cdr:List<T>)
  }

  // A list node with reference semantics--copying the node creates a node
  // that shares structure with the tail of the list
  union Node<T> {
    case Nil()
    case Cons(car:T, ref cdr:Node<T>)
  }

A special ``union`` type is the nullable type ``T?``, which is
sugar syntax for a generic union type ``Nullable<T>``. Since both nullable
refs and refs-that-are-nullable are useful, we could provide sugar syntax for
both to avoid requiring parens::

  ref? Int // Nullable reference to Int: Nullable<ref T>
  ref Int? // Reference to nullable Int: ref Nullable<T>
  val? SomeClass // Nullable SomeClass value: Nullable<val T>
  val Int? // nullable Int: val Nullable<T> -- the default for Nullable<T>

__ non-copyable_

Function Parameters
===================

Function parameters can be explicitly declared ``val``, or ``ref``::

  func baz(
      _ x: Int      // x is passed by-value
    , val y: Int  // just like "y: Int"
    , ref z: Int  // allocate z on the heap

    , q: SomeClass               // passing a reference
    , ref r: SomeClass           // just like "var r: SomeClass"
    , val s: SomeClonableClass) // Passing a copy of the argument

.. Note:: We suggest allowing explicit ``var`` function parameters for
          uniformity.

Semantics of passing arguments to functions follow those of
assignments and initializations: when a ``val`` is involved, the
argument value is copied.

.. Note::

  We believe that ``[inout]`` is an independent concept and still very
  much needed, even with an explicit ``ref`` keyword.  See also the
  Bikeshed_ discussion at the end of this document.

Generics
========

TODO: Why do we need these constraints?
TODO: Consider generic classes/structs

As with an array's element type, a generic type parameter can also be bound to
a ``ref`` or a ``val`` type.

   var rv = new Array<ref Int>       // Create a vector of Ints-on-the-heap
   var vv = new Array<val SomeClass> // Create a vector that owns its SomeClasses

The rules for declarations in terms of ``ref`` or ``val`` types are that
an explicit ``val`` or ``ref`` overrides any ``val``- or ``ref``-ness of the
type parameter, as follows::

   ref x : T // always declares a ref
   val x : T // always declares a val
   var x : T // declares a val iff T is a val

``ref`` and ``val`` can be specified as protocol constraints for type
parameters::

  // Fill an array with independent copies of x
  func fill<T:val>(_ array:[T], x:T) {
    for i in 0...array.length {
      array[i] = x
    }
  }

Protocols similarly can inherit from ``val`` or ``ref`` constraints, to require
conforming types to have the specified semantics::

  protocol Disposable : ref {
    func dispose()
  }

The ability to explicitly declare ``val`` and ``ref`` allow us to
smooth out behavioral differences between value and reference types
where they could affect the correctness of algorithms.  The continued
existence of ``var`` allows value-agnostic generic algorithms, such as
``swap``, to go on working as before.

.. _non-copyable:

Non-Copyability
===============

A non-``Clonable`` ``class`` is not copyable.  That leaves us with
several options:

1. Make it illegal to declare a non-copyable ``val``
2. Make non-copyable ``val``\ s legal, but not copyable, thus
   infecting their enclosing object with non-copyability.
3. Like #2, but also formalize move semantics.  All ``val``\ s,
   including non-copyable ones, would be explicitly movable.  Generic
   ``var`` parameters would probably be treated as movable but
   non-copyable.

We favor taking all three steps, but it's useful to know that there
are valid stopping points along the way.

Default Initialization of ref
=============================

TODO

Array
=====

TODO: Int[...], etc.

Equality and Identity
=====================

TODO

Why Expand the Type System?
===========================

TODO

Why do We Need ``[inout]`` if we have ``ref``?
==============================================

TODO

Why Does the Outer Qualifier Win?
=================================

TODO


Objective-C Interoperability
============================

Clonable Objective-C classes
-----------------------------

In Cocoa, a notion similar to clonability is captured in the ``NSCopying`` and
``NSMutableCopying`` protocols, and a notion similar to ``val`` instance
variables is captured by the behavior of ``(copy)`` properties. However, there
are some behavioral and semantic differences that need to be taken into account.
``NSCopying`` and ``NSMutableCopying`` are entangled with Foundation's
idiosyncratic management of container mutability: ``-[NSMutableThing copy]``
produces a freshly copied immutable ``NSThing``, whereas ``-[NSThing copy]``
returns the same object back if the receiver is already immutable.
``-[NSMutableThing mutableCopy]`` and ``-[NSThing mutableCopy]`` both return
a freshly copied ``NSMutableThing``. In order to avoid requiring special case
Foundation-specific knowledge of whether class types are notionally immutable
or mutable, we propose this first-draft approach to mapping the Cocoa concepts
to ``Clonable``:

* If an Objective-C class conforms to ``NSMutableCopying``, use the
  ``-mutableCopyWithZone:`` method to fulfill the Swift ``Clonable`` concept,
  casting the result of ``-mutableCopyWithZone:`` back to the original type.
* If an Objective-C class conforms to ``NSCopying`` but not ``NSMutableCopying``,
  use ``-copyWithZone:``, also casting the result back to the original type.

This is suboptimal for immutable types, but should work for any Cocoa class
that fulfills the ``NSMutableCopying`` or ``NSCopying`` contracts without
requiring knowledge of the intended semantics of the class beyond what the
compiler can see.

Objective-C ``(copy)`` properties should behave closely enough to Swift ``val``
properties to be able to vend Objective-C ``(copy)`` properties to Swift as
``val`` properties, and vice versa.

Objective-C protocols
---------------------

In Objective-C, only classes can conform to protocols, and the ``This`` type
is thus presumed to have references semantics. Swift protocols
imported from Objective-C or declared as ``[objc]`` could be conformed to by
``val`` types, but doing so would need to incur an implicit copy to the heap
to create a ``ref`` value to conform to the protocol.

How This Design Improves Swift
==============================

1. You can choose semantics at the point of use.  The designer of a
   type doesn't know whether you will want to use it via a reference;
   she can only guess.  You might *want* to share a reference to a
   struct, tuple, etc.  You might *want* some class type to be a
   component of the value of some other type.  We allow that, without
   requiring awkward explicit wrapping, and without discarding the
   obvious defaults for types that have them.

2. We provide a continuum of strictness in which to program.  If
   you're writing a script, you can go with ``var`` everywhere: don't
   worry; be happy.  If you're writing a large-scale program and want
   to be very sure of what you're getting, you can forbid ``var``
   except in carefully-vetted generic functions.  The choice is yours.

3. We allow generic programmers to avoid subtle semantic errors by
   explicitly specifying value or reference semantics where it
   matters.

4. We move the cases where values and references interact much closer
   to, and arguably into, the "easy" zone.

How This Design Beats Rust/C++/C#/etc.
======================================

* Simple programs stay simple.  Rust has a great low-level memory safety
  story, but it comes at the expense of ease-of-use.  You can't learn
  to use that system effectively without confronting two `kinds`__
  of pointer, `named lifetimes`__, `borrowing managed boxes and
  rooting`__, etc.  By contrast, there's a path to learning swift that
  postpones the ``val``\ /``ref`` distinction, and that's pretty much
  *all* one must learn to have a complete understanding of the object
  model in the "easy" and "safe" zones.

__ https://doc.rust-lang.org/reference.html#pointer-types
__ https://doc.rust-lang.org/book/lifetimes.html
__ https://doc.rust-lang.org/book/box-syntax-and-patterns.html

* Simple programs stay safe.  C++ offers great control over
  everything, but the sharp edges are always exposed.  This design
  allows programmers to accomplish most of what people want to with
  C++, but to do it safely and expressively.  As with the rest of Swift,
  the sharp edges are still available as an opt-in feature, and
  without harming the rest of the language.

* Unlike C++, types meant to be reference types, supporting
  inheritance, aren't copyable by default.  This prevents inadvertent
  slicing and wrong semantics.

* By retaining the ``class`` vs. ``struct`` distinction, we give type
  authors the ability to provide a default semantics for their types
  and avoid confronting their users with a constant ``T*`` vs. ``T``
  choice like C/C++.

* C# also provides a ``class`` vs. ``struct`` distinction with a
  generics system, but it provides no facilities for nontrivial value semantics
  on struct types, and the only means for writing generic
  algorithms that rely on value or reference semantics is to apply a
  blunt ``struct`` or ``class`` constraint to type parameters and limit the
  type domain of the generic. By generalizing both value and reference semantics
  to all types, we allow both for structs with interesting value semantics and
  for generics that can reliably specify and use value or reference semantics
  without limiting the types they can be used with.

``structs`` Really Should Have Value Semantics
==============================================

It is *possible* to build a struct with reference semantics. For
example,

..parsed-literal::

  struct XPair
  {
     constructor() {
         // These Xs are notionally **part of my value**
         first = new X
         second = new X
     }
     **ref** first : X
     **ref** second : X
  }

However, the results can be surprising:

.. parsed-literal::

  **val** a : XPair  // I want an **independent value**, please!
  val b = a          // and a copy of that value
  a.first.mutate()   // Oops, changes b.first!

If ``XPair`` had been declared a class, ::

  val a : XPair      // I want an independent value, please!

would only compile if ``XPair`` was also ``Clonable``, thereby
protecting the user's intention to create an independent value


Getting the ``ref`` out of a ``class`` instance declared ``val``
================================================================

A ``class`` instance is always accessed through a reference, but when
an instance is declared ``val``, that reference is effectively hidden
behind the ``val`` wrapper.  However, because ``this`` is passed to
``class`` methods as a reference, we can unwrap the underlying ``ref``
as follows::

  val x : SomeClass

  extension SomeClass {
    func get_ref() { return this }
  }

  ref y : x.get_ref()
  y.mutate()          // mutates x

Teachability
============

By expanding the type system we have added complexity to the language.
To what degree will these changes make Swift harder to learn?

We believe the costs can be mitigated by teaching plain ``var``
programming first.  The need to confront ``val`` and ``ref`` can be
postponed until the point where students must see them in the
interfaces of library functions.  All the same standard library
interfaces that could be expressed before the introduction of ``val``
and ``ref`` can still be expressed without them, so this discovery can
happen arbitrarily late in the game.  However, it's important to
realize that having ``val`` and ``ref`` available will probably change
the optimal way to express the standard library APIs, and choosing
where to use the new capabilities may be an interesting balancing act.

(Im)Mutability
==============

We have looked, but so far, we don't think this proposal closes (or,
for that matter, opens) the door to anything fundamentally new with
respect to declared (im)mutability.  The issues that arise with
explicit ``val`` and ``ref`` also arise without them.

Bikeshed
========

There are a number of naming issues we might want to discuss.  For
example:

* ``var`` is only one character different from ``val``.  Is that too
  confusable?  Syntax highlighting can help, but it might not be enough.

  * What about ``let`` as a replacement for ``var``?
    There's always the dreaded ``auto``.

  * Should we drop ``let``\ /``var``\ /``auto`` for ivars, because it
    "just feels wrong" there?

* ``ref`` is spelled like ``[inout]``, but they mean very different things

  * We don't think they can be collapsed into one keyword: ``ref``
    requires shared ownership and is escapable and aliasable, unlike
    ``[inout]``.

  * Should we spell ``[inout]`` differently?  I think at a high level
    it means something like "``[rebind]`` the name to a new value."

* Do we want to consider replacing ``struct`` and/or ``class`` with
  new names such as ``valtype`` and ``reftype``?  We don't love those
  particular suggestions.  One argument in favor of a change:
  ``struct`` comes with a strong connotation of weakness or
  second-class-ness for some people.