[swift-evolution] [Review] SE-0026 Abstract classes and methods
Gwendal Roué
gwendal.roue at gmail.com
Fri Mar 4 13:24:14 CST 2016
> Le 4 mars 2016 à 08:45, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> a écrit :
>
>> For modeling subtype requirements we would need the ability to declare protocol members with reduced access scope like private or internal and hopefully protected, as subtype requirements most often are not public.
>
> I've spoken about this elsewhere, but:
>
> - For both abstract classes and protocols, all required/abstract members need to be visible everywhere you're permitted to conform/inherit.
> - There is currently no way in Swift to decouple a protocol's/class's visibility from the ability to conform to/inherit from it, so neither construct can currently offer this feature.
> - However, this feature is likely to come at least for classes as part of resiliency. I think it's a good idea for protocols, too.
Brent, please consider the sample code below:
// Framework land
public abstract class DatabaseRecord {
// DatabaseRow initializer
public required init(row: DatabaseRow) {
referenceRow = row
}
// The table name
public abstract class func databaseTableName() -> String
// What should be stored in the database
public abstract var persistedRow: DatabaseRow
// True if record has been changed since last fetch
public var hasChanges: Bool {
// return complex computation based on referenceRow and persistedRow
}
// Hidden implementation detail
internal var referenceRow: DatabaseRow
}
// User land
class Person : DatabaseRecord {
var name: String
class func databaseTableName() -> String { return "persons" }
init(row: DatabaseRow) {
name = row["name"]
super.init(row)
}
var persistedRow: DatabaseRow { return Row("name": name) }
}
let person = Person(row: …)
person.name // "foo"
person.hasChanges // false
person.name = "bar"
person.hasChanges // true
How do we express this with protocols?
// Framework land
public protocol DatabaseRecord {
var referenceRow: DatabaseRow { get set }
var persistedRow: DatabaseRow { get }
static func databaseTableName() -> String
init(row: DatabaseRow)
}
extension DatabaseRecord {
public var hasChanges: Bool {
// return complex computation based on referenceRow and persistedRow
}
}
// User land
class Person: DatabaseRecord {
var referenceRow: DatabaseRow
var name: String
init(row: DatabaseRow) {
name = row["name"]
super.init(row)
}
class func databaseTableName() -> String { return « persons" }
var persistedRow: DatabaseRow { return Row("name": name) }
}
let person = Person(row: …)
person.name // "foo"
person.hasChanges // false
person.name = "bar"
person.hasChanges // true
person.referenceRow = … // messed up internal state
The problem with protocols is that the implementation detail referenceRow has to be public (when it should remain an implementation detail), and that the conforming types must provide it (when they should not even know about its existence).
So abstract classes and protocols are NOT on the same stage today regarding members visibility.
Gwendal
More information about the swift-evolution
mailing list