>> * Does it have to be a protocol? Why not also allow the concrete type of the
>> property you're forwarding to? Obviously you couldn't form a subtype
>> relationship (unless you could...), but this might be useful to reduce
>> boilerplate when you're proxying something.
>
> This is addressed in the alternatives considered section.
Sorry, I missed that, probably because the sample code in that section didn't
show such a forwarding.
> The short answer is that there the direct interface of the concrete type does
> not contain sufficient information about potential Self parameters to do this
> well. This information does exist in the protocol declarations. Allowing
> this information to be specified in concrete interfaces would add enough
> complexity to the language that I don’t think it is worthwhile.
That's a good point. You could perhaps add a way to tweak the forwarding of
certain members, but that'd be a little tricky.
One of the things I'd like to see is the ability to proxy for an instance
without the person writing the proxy knowing which instances it'll be used
with. Think, for example, of the Cocoa animator proxy, or
`NSUndoManager.prepareWithInvocationTarget(_:)`. It'd be nice if a Swift
equivalent could return, say, `NSAnimatorProxy<View>` or
`NSUndoManager.InvocationTarget<Target>`, which has all the methods of the
generic type but records the calls for later use.
Of course, what you really want is for only a particular subset of the methods
to be available on the proxy (animated methods on `NSAnimatorProxy`, Void
methods on `NSUndoManager.InvocationTarget`), and of course in these cases
you're not calling directly through to the underlying methods. So I might just
be barking up the wrong tree here.
>> * Why the method-based conversion syntax for return values, rather than
>> something a little more like a property declaration?
>>
>> var number: Int
>> forward IntegerType to number {
>> static return(newValue: Int) {
>> return NumberWrapper(newValue)
>> }
>> return(newValue: Int) {
>> return NumberWrapper(newValue)
>> }
>> }
>
> This is actually a really good idea to consider! I didn’t consider something
> like this mostly because I didn’t think of it. I’m going to seriously
> consider adopting an approach along these lines.
Great.
> One possible advantage of the approach I used is that the initializer may
> already exist for other reasons and you would not need to do any extra work.
True. But it may also exist and *not* do what you want in the forwarding case.
It's easier to explicitly use the right initializer than it is to work around
the forwarding system implicitly using the wrong one.
> A big advantage of this approach is that it would work even when you are
> forwarding different protocols to more than one member with the same type.
But again, if that's the wrong behavior, there's no good way to fix it.
>> * If you want to keep the method-based syntax, would it make sense to
>> instead have an initializer for instance initializers too, and just have it
>> take a second parameter with the instance?
>>
>> init(forwardedReturnValue: Int) {...}
>> init(forwardedReturnValue: Int, from: NumberWrapper) {…}
>
> Part of the reason the instance method was used is because sometimes the
> right thing to do might be to mutate and then return self. Using an instance
> method gives you the flexibility to do that if necessary.
In practice, I'm not sure that's actually the case very often. How frequently
will the internal type return a changed value, but your identically-named cover
method ought to mutate the property? That completely changes the semantics of
the underlying call.
I mean, what you're proposing would be something like this:
class ListOfThings {
private var actualList: [Thing]
func filter(predicate: Thing -> Bool) -> ListOfThings {
let returnValue = actualList.filter(predicate)
actualList = returnValue
return ListOfThings(returnValue)
}
}
Is that a thing you actually expect people to do?
>> * Does this mean that a `public forward` declaration would forward
>> `internal` members through synthesized `public` interfaces, if the forwarder
>> and forwardee happened to be in the same module?
>>
>>> All synthesized members recieve access control modifiers matching the
>>> access control modifier applied to the forward declaration.
>
> Yes, if the forwardee had internal visibility and the forwarder was public
> the forwarder could publicly forward the interface. This is intentional
> behavior. The forwardee may well be an internal implementation detail while
> the methods of the protocol are part of the public interface of the
> forwarder. It is possible to write code that does this manually today.
I suppose that, if it's always a protocol you're forwarding to, you can assume
that none of the protocol methods are internal-only implementation details. But
I have to admit that I'm still concerned about this; it just seems like a
recipe for accidentally exposing things you meant to keep private.
>> * You don't explicitly mention this, but I assume mutating methods work and
>> mutate `self`?
>
> Mutating methods are something I didn’t think about carefully yet. Thanks
> for pointing that out! But generally, yes a forwarding implementation of a
> mutating method would need to mutate the forwardee which is part of self,
> thus mutating self.
Well, as long as they're thought about at some point!
--
Brent Royal-Gordon
Architechies
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution