CustomPlaygroundRepresentationConvertible? Saagar Jha
> On Jan 11, 2018, at 11:22, Connor Wakamo via swift-evolution > <swift-evolution@swift.org> wrote: > > > >> On Jan 10, 2018, at 4:21 PM, Chris Lattner <clatt...@nondot.org >> <mailto:clatt...@nondot.org>> wrote: >> >>> On Jan 10, 2018, at 2:10 PM, Connor Wakamo <cwak...@apple.com >>> <mailto:cwak...@apple.com>> wrote: >>>> What is the use-case for a type conforming to this protocol but returning >>>> nil? If there is a use case for that, why not have such an implementation >>>> return “self” instead? >>> >>> Riley and Saagar answered this down-thread, but to confirm — returning nil >>> would allow some instances of a type to use the “default” playground >>> logging presentation while others use an alternate presentation instead. >> >> Right, this gets back to the question: what is the use case for this? When >> would a type want to “sometimes” replace the default representation? >> >> It seems to me that a type author either wants to take control of >> presentation or not. While I’m sure we could imagine some use case for the >> behavior you’re describing, is it big enough to make it worth complicating >> the API? >> >>> >>> This isn’t handled by `return self` because, unless I’m mistaken, there’s >>> no way to detect that from the caller’s side (e.g. with two `Any` values, I >>> can’t do `self === self.playgroundRepresentation`). >> >> Ok >> >>>> In short, can we change playgroundRepresentation to return Any instead of >>>> Any?. Among other things, doing so could ease the case of playground >>>> formatting Optional itself, which should presumably get a conditional >>>> conformance to this. :-) >>> >>> I don’t think we can change this to return `Any` instead of `Any?`. I think >>> there are potentially cases where a developer might want to selectively >>> opt-in to this behavior. >> >> Which cases? How important are they? > > I can think of a couple of cases where this could be useful. > > The first is an enum. Extending Riley’s example from earlier in the thread: > > enum MyUnion { > case string(String) > case image(UIImage) > case intPair(Int, Int) > case none > } > > This enum might want to present the string and image cases as strings and > images, but treat the intPair and none cases the “default” way for the enum. > This is probably not the most compelling example as there is a workaround — > return a second enum or other structured type from playgroundRepresentation — > but that feels not great. > > The second case, and the one I’m more worried about, is subclassing. If, for > instance, you have the following: > > class FooView: UIView, CustomPlaygroundRepresentable { > var playgroundRepresentation: Any { > return “A string describing this view" > } > } > > class BarView: FooView { > override var playgroundRepresentation: Any { > // BarView instances wanted to be logged as themselves, > but there’s no way to express that > return ??? > } > } > > There’s nothing that BarView can do to ensure it gets logged like a view > because FooView declared a conformance to CustomPlaygroundRepresentable. > > I’m not sure how important these cases are. If it were just the first one, > I’d probably say we can safely ignore it because there’s a reasonable > workaround. But the subclassing case worries me because there’s no escape > hatch (aside from a potential implementation-defined failsafe). > > >>> I also don’t think that `Optional` would get a conditional conformance to >>> this. I’m not proposing that any standard library or corelibs types gain >>> conformances to this protocol. Instead, it’s up to a playground logger >>> (such as PlaygroundLogger in swift-xcode-playground-support >>> <https://github.com/apple/swift-xcode-playground-support>) to recognize >>> these types and handle them accordingly. The playground logger would look >>> through the `Optional` so that this would effectively be true, but ideally >>> the log data generated by a logger would indicate that it was wrapped by >>> `Optional.some`. >> >> Why not? I understand that that is how the old algorithm worked, but it >> contained a lot of special case hacks due to the state of Swift 1 :-). This >> is a chance to dissolve those away. > > It’s a feature that Optional (and other standard library/corelibs/OS types) > don’t conform to CustomPlaygroundRepresentable. In my mind, it’s the role of > the PlaygroundLogger library to understand the types for which it wishes to > generate an opaque representation instead of the standard/fallback structured > representation. So Optional, String, Int, UIColor, NSView, etc. don’t > themselves conform to CustomPlaygroundRepresentable — they’re not customizing > their presentation in a playground. > > Semi-related to this proposal, I’m working on a rewrite of the > PlaygroundLogger library (currently at > <https://github.com/cwakamo/swift-xcode-playground-support/tree/runtime-framework-and-improved-logger > > <https://github.com/cwakamo/swift-xcode-playground-support/tree/runtime-framework-and-improved-logger>>) > which makes it so that the only special standard library behavior it depends > on is Mirror — it no longer relies on the Swift runtime (via > PlaygroundQuickLook(reflecting:)) to figure out what to log, instead opting > to check protocol conformances itself. So if this proposal is accepted into > Swift, concurrent with that we’ll have a new PlaygroundLogger implementation > which gets rid of as many hacks as possible. > >> To be clear, I would expect that the conformance for optional would be >> defined in the playground module along with this protocol - it wouldn’t be >> defined in the standard library itself. >> >>> >>> One possibility would be to change the API so that it returns an enum. >>> Imagine: >>> >>> enum PlaygroundLoggingBehavior { >>> /// Asks the playground logger to generate the standard logging >>> for `self`. >>> case standard >>> >>> /// Asks the playground logger to generate logging for the >>> given `Any` instead of `self`. >>> case custom(Any) >>> } >>> >>> protocol CustomPlaygroundLoggable { >>> /// Returns the `PlaygroundLoggingBehavior` to use for `self`. >>> var playgroundLoggingBehavior: PlaygroundLoggingBehavior { get } >>> } >>> >>> (To Saagar’s point in another email — you could even add a `case none` to >>> PlaygroundLoggingBehavior to inhibit logging of a particular instance.) >>> >>> `CustomPlaygroundLoggable` would be a little clunkier to implement than >>> `CustomPlaygroundRepresentable` is, as in the common case folks would have >>> to write `return .custom(…)`. It’s possible that the clarity and additional >>> flexibility this grants outweighs that cost; I’m not sure, and would love >>> feedback on that. >> >> I just don’t understand the usecase for “conditional customizing” at all. >> By way of example, we don’t have the ability to do that with >> CustomStringConvertible. What is different about this case? > > I think the big difference with CustomStringConvertible is that it’s possible > for a conformance to reimplement the default behavior on its own. For > instance, if I have: > > enum Foo { > case one(Any) > case two > } > > As noted above, recovering the default behavior with > CustomPlaygroundRepresentable is not always possible if the return type is > `Any`. That very well might be an acceptable trade-off to keep the API simple. > >>>>> /// Implementors of `CustomPlaygroundRepresentable` may return a value of >>>>> one of >>>>> /// the above types to also receive a specialized log representation. >>>>> /// Implementors may also return any other type, and playground logging >>>>> will >>>>> /// generated structured logging for the returned value. >>>>> public protocol CustomPlaygroundRepresentable { >>>> On the naming bikeshed, the closest analog to this feature is >>>> CustomStringConvertible, which is used when a type wants to customize the >>>> default conversion to string. As such, have you considered >>>> CustomPlaygroundConvertible for consistency with it? >>>> >>>> The only prior art for the word “Representable” in the standard library is >>>> RawRepresentable, which is quite a different concept. >>>> >>>>> /// Returns the custom playground representation for this instance, or >>>>> nil if >>>>> /// the default representation should be used. >>>>> /// >>>>> /// If this type has value semantics, the instance returned should be >>>>> /// unaffected by subsequent mutations if possible. >>>>> var playgroundRepresentation: Any? { get } >>>> Again to align with CustomStringConvertible which has a ‘description’ >>>> member, it might make sense to name this member “playgroundDescription”. >>> >>> I’m definitely open to different names for this. >>> (`CustomPlaygroundRepresentable` was inspired by the API I’m removing, >>> `CustomPlaygroundQuickLookable`, as they both take their sole property and >>> make them -able.) >>> >>> I do like the `playgroundDescription` name for the property, but am a >>> little hesitant to use the name `CustomPlaygroundConvertible` because >>> conforming types can’t be converted to playgrounds. I can’t come up with an >>> appropriate word in `CustomPlaygroundThingConvertible` to use in place of >>> `Thing`, though. (If we end up pivoting to the enum I described above then >>> something like `CustomPlaygroundLoggable` would be more appropriate.) >> >> I would strongly recommend aligning with the state of the art in >> CustomStringConvertible (which has been extensively discussed) and ignore >> the precedent in the existing playground logging stuff (which hasn’t). > > That’s very reasonable. I’ll update the proposal to use > CustomPlaygroundConvertible (unless I or someone else can come up with a > really good “Thing” for a name like CustomPlaygroundThingConvertible, as that > would even better match CustomStringConvertible/CustomDebugStringConvertible). > > Connor > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org <mailto:swift-evolution@swift.org> > https://lists.swift.org/mailman/listinfo/swift-evolution > <https://lists.swift.org/mailman/listinfo/swift-evolution>
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution