> On Nov 28, 2017, at 8:35 PM, Chris Lattner <[email protected]> wrote:
>
> We’ve had a lot of discussions over the years about how to balance simplicity
> vs power, implicitness vs explicitness, intentionality vs accidental
> behavior, etc. For example, in very early discussions about Swift generics,
> some folks where strong proponents of protocol conformance being fully
> implicit: satisfying all the requirements of a protocol meant that you
> conformed to it, even if you didn’t explicitly “inherit” from it.
>
> This is obviously not the design we went with over the long term, and I’m
> glad we didn’t.
I get that, but an attribute in particular would be just as explicit as a
protocol conformance.
> I think that DynamicMemberLookup requiring conformance is the same thing: it
> makes it explicit that the behavior is intentional, and it allows somewhat
> better error checking (if you conform to the protocol but don’t implement the
> (implicitly known) requirement, you DO get an error). That said, this is
> just my opinion.
So you envision that the compiler knows that
`DynamicMemberLookupProtocol`-conforming types ought to have
`subscript(dynamicMember:)` members, and these members ought to have certain
traits (unary, ExpressibleByStringLiteral parameter, etc.), and it should
enforce those traits, even though there's no matching requirement in the
protocol?
That seems like a lot of compiler magic to attach to a particular protocol. A
`@dynamicMember` attribute would have a similar amount of magic, but people
*expect* that kind of magic from an attribute. For instance, the `@objc`
attribute places lots of conditions on the types of parameters, etc., and
nobody is surprised by that.
Other reasons to prefer an attribute:
* I can't think of a use case where you would want dynamic members but not want
to make the subscript itself accessible. If `@dynamicMember` could be applied
to any subscript, then you could use any label (or no label) for sugar-free use
of the dynamic functionality. For instance, the JSON example could decorate its
existing subscript instead of introducing a redundant one:
extension JSON {
var stringValue : String? {
if case .StringValue(let str) = self {
return str
}
return nil
}
subscript(index: Int) -> JSON? {
if case .ArrayValue(let arr) = self {
return index < arr.count ? arr[index] : nil
}
return nil
}
@dynamicMember subscript(key: String) -> JSON? {
if case .DictionaryValue(let dict) = self {
return dict[key]
}
return nil
}
}
* If we eventually want to support multiple subscripts with different types
(either by using different return types, or by later supporting
non-`ExpressibleByStringLiteral` fixed attribute sets), allowing multiple
subscripts to be annotated by `@dynamicMember` is more natural than allowing
multiple `subscript(dynamicMember:)` members to simultaneously satisfy a single
protocol pseudo-requirement.
The only thing you're using the `DynamicMemberLookupProtocol` for is to mark
the type for the compiler to recognize; you're not using any of the other
capabilities of protocols. That would be okay if you were proposing something
that wouldn't touch the compiler if implemented with a protocol, but this
feature definitely requires significant compiler work. It'd be okay if all you
needed to do was mark a type, but this feature also requires you to implement
certain members, following certain conventions, which happen to be difficult to
express through protocols. It'd be okay if a type needed many members to meet
the requirements, but it only needs one. It'd be okay if you needed to
constrain generic calls to require conformance to the type, but there's no need
for that here.
Basically, you're using a hammer to drive a screw. Why not use a screwdriver
instead?
--
Brent Royal-Gordon
Architechies
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution