Swift, and no usable language, is completely statically typed. Examples
in Swift of runtime type checking are array out of bounds and casts. There
are other examples of non-type related runtime checks is Swift: numerical
overflow, throws, using optionals to signal errors, and using enums to
signal errors. I say use what is appropriate, static type checking if it is
easy to do, otherwise runtime type checking. Note I am not proposing an
unsafe language like C, it is still type checked.
-- Howard.
On 13 January 2016 at 11:33, Simon Pilkington < >>> simonmpilkington@icloud.com> wrote:
The problem is that conceptually and behaviourally Box<Bottom> *is
indeed not* a Box<Top> and cannot be treated the same as one. The proposal
attempts to get around this difference with a runtime failure but this
would result in very fragile code - you get passed a Box<Top> and want to
pass it a subclass of Top, will it succeed, who knows. You probably would
be able to check the types but the complier wouldnât highlight that this is
an operation that could potentially fail.
This seems to be very much against Swiftâs goal of safety being
enforced by the compiler as much as possible.
Java uses the wildcard syntax to highlight this conceptual and
behavioural difference - Box<Bottom> is not covariant with Box<Top> but
rather with Box<? extends Top>. The compiler can then enforce that a
programmer doesnât try to pass an incompatible type to a variable of such
type. Even though this is a complication to the language (many Java
programmers struggle with correctly using the wildcard syntax) I donât see
covariance for generics being added to Swift in a robust manner without
some kind of similar syntax.
-Simon
On 12 Jan 2016, at 8:45 PM, Howard Lovatt via swift-evolution < >>>> swift-evolution@swift.org> wrote:
Currently you generics are invariant whereas function arguments etc.
are covariant. I am suggesting that if the way generics are implemented is
changed then they can be made covariant and that this will add considerable
utility to Swift generics.
1st a demonstration of the current situation of invariant generics:
// Current system
class Top {}
class Bottom: Top {}
struct Box<T: AnyObject> {
var value: T
init(_ initialValue: T) {
value = initialValue;
}
}
let boxB = Box(Bottom())
// let boxT: Box<Top> = boxB // Covariance currently not allowed
The key point is although `Bottom` 'is aâ `Top`, `Box<Bottom>` *is not*
a `Box<Top>`.
I am suggesting:
1. That `Box<Bottom>` should be a `Box<Top>` (covariance).
2. An implementation that allows the above covariance.
3. That protocols are made generic, i.e. `protocol Box<T> { var value:
T { get set } }` and that this mechanism replaces associated types for
protocols.
// Proposal:
// 1. No change to Box, i.e. programmer would just write Box as
before
// 2. Code transformed by comiler with write check for each
specific, generic type instance
// Best approximation of resulting code in current Swift to
demonstrate spirit of idea:
// Compiler writes a universal form using the upper bound (it
writes the underlyting representation).
// In practice this would be called `Box` but used `BoxAnyObject`
to indicate that it has a generic argument bounded by `AnyObject`.
struct BoxAnyObject {
// Generated from generic argument `<T: AnyObject>`.
let T: AnyObject.Type // Store the actual type.
// Generated from stored property `var value: T` and noting
that `T`'s upper bound is `AnyObject`.
private var _value: AnyObject // Access the stored property
through a setter so that type can be checked
var value: AnyObject {
get {
return _value
}
set {
// In all functions check that args declared as `T`
are actually a `T` or a sub-type.
// Note: `is` only works with type literal and there
is no `>=` operator for types :(.
// `is` would need changing or `>=` for types adding,
nearest at moment `==`.
precondition(T == /* >= */ newValue.dynamicType, "Type
of newValue, \(newValue.dynamicType), is not a sub-type of generic
type T, \(T)")
_value = newValue
}
}
// Generated from `init(_ initialValue: T)` and noting that
`T`'s upper bound is `AnyObject`.
init(_ lowestCommonDeclaredT: AnyObject.Type, _ initialValue:
AnyObject) {
T = lowestCommonDeclaredT
_value = initialValue
}
}
// Demonstrate that all `Box`es are the same size and therefore
can be bitwise copied
// Compiler supplies lowest-common, declared, generic type for all
the `T`s in the `init` call.
var bT = BoxAnyObject(Top.self, Top()) // In practice user would
write `let bT = Box(Top())`.
bT.T // Top.Type
sizeofValue(bT) // 16
var bB = BoxAnyObject(Bottom.self, Bottom()) // In practice user
would write `let bB = Box(Bottom())`.
bB.T // Bottom.Type
sizeofValue(bB) // 16
// Demonstration covariance.
bT = bB // Compiler would check covariance of declared generic
types.
bT.T // Bottom.Type
// Demonstrate generic returned type
// Compiler would add cast to declared, generic type.
bB.value as! Bottom // In practice user would write `bB.value`.
// Demonstrate type safety
bT = BoxAnyObject(Top.self, Top()) // In practice user would write
`bT = Box(Top())`.
bT.value = Top() // OK
// bT.value = Bottom() // Doesn't work at present because need
`>=` for types, but would work in practice
// bB.value = Top() // Runtime error - wrong type
The implications of this proposal are:
1. The compiler can statically type check a read from a stored property.
2. A write to a stored property is type checked at runtime.
3. Protocols can be made generic instead of having an associated type
and then they become a proper type with dynamic dispatch.
4. Generic protocols can be a type just like non-generic protocols,
structs, and classes and unlike associated type protocols that can only be
a generic constraint.
5. The awkwardness of dealing with associated type generics is replaced
by a more powerful and easier to understand semantic of a type, just like
the other types.
6. There is a lot of ânon-obvoiusâ, long code, for example `inits`,
that use a `where` clause to constrain an associated type protocol, this
would be unnecessary.
7. There are whole types, `AnySequence`, `AnyGenerator`, etc., that
would be replaced by a generic protocols, `Sequence`, `Generator`, etc.
Advantages:
1. Covariant generics are a powerful addition to the language.
2. Genericsâ invariance are inconsistent with the rest of the language.
3. Generic protocols would become a âproperâ type and you could have
arrays and fields of a generic protocol.
4. There are many threads on swift-evolution looking at how protocols
can be made into a âproperâ type or at least a concept that is easier to
understand.
Compatibility:
1. This would be a major change since associated types in protocols
would be replaced by generics.
2. The new implementation of generics might break some existing
`struct` and `class` code, for example if it is dependent on the exact size
of an object because the class will have extra fields, one for each generic
type, and therefore will be larger.
Disadvantages:
1. Major change.
2. Object size increases.
Thanks in advance for any comments,
â Howard.
PS This is part of a collection of proposals previously presented as
âProtocols on Steroidsâ.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution