[swift-evolution] [swift-evolution-announce] [Review] SE-0089: Replace protocol<P1, P2> syntax with Any<P1, P2>

Russ Bishop xenadu at gmail.com
Thu Jun 9 13:56:16 CDT 2016


> On Jun 9, 2016, at 9:42 AM, Dave Abrahams via swift-evolution <swift-evolution at swift.org> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160609/f5ded78d/attachment.html>


More information about the swift-evolution mailing list