> On Jun 9, 2016, at 9:42 AM, Dave Abrahams via swift-evolution
> <[email protected]> wrote:
>
>
> If your code has many manual type erasing wrappers corresponding to
> protocols with associated types and/or Self requirements that also never
> have to trap type mismatches, that would certainly be instructive
> empirical data. Would you care to share the protocols and wrappers you
> are talking about?
>
>
> --
> Dave
Part of the problem is you get used to living inside the cell and forget what
life might be like on the outside. I don’t run in to many cases anymore because
I know they won’t work and pre-emotively constrain my designs to fit into the
existing restrictions.
Sorry for the long bit of code, I tried to boil it down to the absolute
minimum. It has the advantage of being actual shipping code:
public protocol AnyMutationOperation {
func __mutate(object: Any) -> Any
}
public protocol MutationOperation: AnyMutationOperation {
associatedtype ObjectType: MutatableSyncable
func _mutate(object: ObjectType) -> ObjectType
}
public protocol MutatableSyncable {
var _operations: [AnyMutationOperation] { get set }
mutating func mutate<T: MutationOperation where T.ObjectType ==
Self>(mutation: T) -> Self
}
extension MutatableSyncable {
mutating func mutate<T: MutationOperation where T.ObjectType ==
Self>(mutation: T) -> Self {
self._operations.append(mutation)
return mutation._mutate(self)
}
mutating func _mutate(mutation: AnyMutationOperation) -> Self {
return mutation.__mutate(self) as! Self
}
}
extension MutationOperation {
func __mutate(object: Any) -> Any {
return self._mutate(object as! ObjectType)
}
}
struct Model: MutatableSyncable {
var _operations: [AnyMutationOperation] = []
var name: String = ""
}
struct ChangeNameOperation: MutationOperation {
var newName: String
func _mutate(object: Model) -> Model {
var copy = object
copy.name = self.newName
return object
}
}
AnyMutationOperation, the force casting, etc are all to work around the
inability to call MutatableSyncable.mutate() with MutationOperations that came
from the database.
// at local point of mutation, works fine
var model = Model()
let op = ChangeNameOperation(newName: "bob")
model.mutate(op)
// when trying to rollback/rollforward in sync manager, which handles many
model and mutation types
var model: MutatableSyncable = …
// this can't even be MutationOperation
let op: AnyMutationOperation = …
// ERROR, can't do that!
//model.mutate(op)
// Better hope none of those force casts trap!
model._mutate(op)
We didn’t want to encode a massive list of 100s of types in a big ol’ switch
statement because we’re modeling every single mutable field as a separate
MutationOperation. All we know in the sync manager is we have a
MutatableSyncable and some MutationOperation (but in our type-erased
AnyMutationOperation). So we have a fragile system that traps if anyone makes a
mistake. I’d much rather be able to unwrap the existential and validate that
the operation’s ObjectType matches the model’s concrete type, then call the
generic version of mutate().
There is another place where we want to find the ObjectManager<T: ModelType>
for the model type T, then call it but we run into the same associated types
problem. We couldn’t create a Dictionary<ModelType, ObjectManager<ModelType>>
because ObjectManager has associated types.
In that case the list of model types was constrained enough we went for the big
switch statement but it takes Any so is completely unchecked by the compiler
even though we know that we could constrain it to be type safe:
func saveFromServer(model: Any) throws {
switch model {
case let model as PGReportTypeFullModel:
try self.reportTypeManager.saveFromServer(model)
// lots of others here ...
}
}
I’ll see if I can find some more examples later.
Russ_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution