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

Reply via email to