<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">CustomPlaygroundRepresentationConvertible?<div class=""><br class=""><div class="">
<div style="color: rgb(0, 0, 0); letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Saagar Jha</div>

</div>
<div><br class=""><blockquote type="cite" class=""><div class="">On Jan 11, 2018, at 11:22, Connor Wakamo via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class="Apple-interchange-newline"><br class=""><blockquote type="cite" class=""><div class="">On Jan 10, 2018, at 4:21 PM, Chris Lattner &lt;<a href="mailto:clattner@nondot.org" class="">clattner@nondot.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class="">On Jan 10, 2018, at 2:10 PM, Connor Wakamo &lt;<a href="mailto:cwakamo@apple.com" class="">cwakamo@apple.com</a>&gt; wrote:</div><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class="">What is the use-case for a type conforming to this protocol but returning nil? &nbsp;If there is a use case for that, why not have such an implementation return “self” instead?</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">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.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Right, this gets back to the question: what is the use case for this? &nbsp;When would a type want to “sometimes” replace the default representation?</div><div class=""><br class=""></div><div class="">It seems to me that a type author either wants to take control of presentation or not. &nbsp;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?</div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class=""><br class=""></div><div class="">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`).<span class="Apple-converted-space">&nbsp;</span></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Ok</div><div class=""><br class=""></div><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class="">In short, can we change&nbsp;playgroundRepresentation to return Any instead of Any?. &nbsp;Among other things, doing so could ease the case of playground formatting Optional itself, which should presumably get a conditional conformance to this. &nbsp;:-)</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">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.</div></div></div></div></blockquote><div class=""><br class=""></div>Which cases? &nbsp;How important are they?</div></div></div></blockquote><div class=""><br class=""></div><div class="">I can think of a couple of cases where this could be useful.</div><div class=""><br class=""></div><div class="">The first is an enum. Extending Riley’s example from earlier in the thread:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>enum MyUnion {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case string(String)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case image(UIImage)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case intPair(Int, Int)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case none</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">The second case, and the one I’m more worried about, is subclassing. If, for instance, you have the following:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>class FooView: UIView, CustomPlaygroundRepresentable {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>var playgroundRepresentation: Any {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>return “A string describing this view"</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>class BarView: FooView {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>override var playgroundRepresentation: Any {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>// BarView instances wanted to be logged as themselves, but there’s no way to express that</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                        </span>return ???</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>}</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class="">There’s nothing that BarView can do to ensure it gets logged like a view because FooView declared a conformance to CustomPlaygroundRepresentable.</div><div class=""><br class=""></div><div class="">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).</div><div class=""><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class="">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&nbsp;<a href="https://github.com/apple/swift-xcode-playground-support" class="">swift-xcode-playground-support</a>) 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`.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Why not? &nbsp;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 :-). &nbsp;This is a chance to dissolve those away.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">Semi-related to this proposal, I’m working on a rewrite of the PlaygroundLogger library (currently at &lt;<a href="https://github.com/cwakamo/swift-xcode-playground-support/tree/runtime-framework-and-improved-logger" class="">https://github.com/cwakamo/swift-xcode-playground-support/tree/runtime-framework-and-improved-logger</a>&gt;) 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.</div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class="">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.</div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class=""><br class=""></div><div class=""><div class="">One possibility would be to change the API so that it returns an enum. Imagine:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>enum PlaygroundLoggingBehavior {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>/// Asks the playground logger to generate the standard logging for `self`.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case standard</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>/// Asks the playground logger to generate logging for the given `Any` instead of `self`.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case custom(Any)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>protocol CustomPlaygroundLoggable {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>/// Returns the `PlaygroundLoggingBehavior` to use for `self`.</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>var playgroundLoggingBehavior: PlaygroundLoggingBehavior { get }</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class="">(To Saagar’s point in another email — you could even add a `case none` to PlaygroundLoggingBehavior to inhibit logging of a particular instance.)</div><div class=""><br class=""></div><div class="">`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.</div></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">I just don’t understand the usecase for “conditional customizing” at all. &nbsp;By way of example, we don’t have the ability to do that with&nbsp;CustomStringConvertible. &nbsp; What is different about this case?</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>enum Foo {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case one(Any)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">                </span>case two</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div><div class="">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.</div><br class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class="highlight highlight-source-swift" style="box-sizing: border-box; margin-bottom: 16px; color: rgb(36, 41, 46); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;; font-size: 16px; background-color: rgb(255, 255, 255);"><pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, Courier, monospace; font-size: 13.600000381469727px; margin-top: 0px; margin-bottom: 0px; word-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal;"><span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> Implementors of `CustomPlaygroundRepresentable` may return a value of one of</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> the above types to also receive a specialized log representation.</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> Implementors may also return any other type, and playground logging will</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> generated structured logging for the returned value.</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"></span><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">public</span> <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">protocol</span> <span class="pl-en" style="box-sizing: border-box; color: rgb(111, 66, 193);">CustomPlaygroundRepresentable</span> {
</pre></div></div></div></blockquote><div class="">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. &nbsp;As such, have you considered CustomPlaygroundConvertible for consistency with it?</div><div class=""><br class=""></div><div class="">The only prior art for the word “Representable” in the standard library is RawRepresentable, which is quite a different concept.</div><div class=""><br class=""></div><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class=""><div class="highlight highlight-source-swift" style="box-sizing: border-box; margin-bottom: 16px; color: rgb(36, 41, 46); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;, &quot;Segoe UI Symbol&quot;; font-size: 16px; background-color: rgb(255, 255, 255);"><pre class="" style="box-sizing: border-box; font-family: SFMono-Regular, Consolas, &quot;Liberation Mono&quot;, Menlo, Courier, monospace; font-size: 13.600000381469727px; margin-top: 0px; margin-bottom: 0px; word-wrap: normal; padding: 16px; overflow: auto; line-height: 1.45; background-color: rgb(246, 248, 250); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal;">  <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> Returns the custom playground representation for this instance, or nil if</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"></span>  <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> the default representation should be used.</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"></span>  <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);">///</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"></span>  <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> If this type has value semantics, the instance returned should be</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"></span>  <span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"><span class="pl-c" style="box-sizing: border-box;">///</span> unaffected by subsequent mutations if possible.</span>
<span class="pl-c" style="box-sizing: border-box; color: rgb(106, 115, 125);"></span>  <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">var</span> playgroundRepresentation<span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">:</span> <span class="pl-c1" style="box-sizing: border-box; color: rgb(0, 92, 197);">Any</span><span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">?</span> { <span class="pl-k" style="box-sizing: border-box; color: rgb(215, 58, 73);">get</span> }
</pre></div></div></div></blockquote><div class="">Again to align with&nbsp;CustomStringConvertible which has a ‘description’ member, it might make sense to name this member “playgroundDescription”.</div></div></div></div></blockquote><div class=""><br class=""></div><div class="">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.)</div><div class=""><br class=""></div><div class="">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.)</div></div></div></div></blockquote><br class=""></div><div class="">I would strongly recommend aligning with the state of the art in&nbsp;CustomStringConvertible (which has been extensively discussed) and ignore the precedent in the existing playground logging stuff (which hasn’t).</div></div></div></blockquote><div class=""><br class=""></div><div class="">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).</div><div class=""><br class=""></div><div class="">Connor</div></div><span style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">_______________________________________________</span><br style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">swift-evolution mailing list</span><br style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><a href="mailto:swift-evolution@swift.org" style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">swift-evolution@swift.org</a><br style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" style="font-family: SFProText-Regular; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;" class="">https://lists.swift.org/mailman/listinfo/swift-evolution</a></div></blockquote></div><br class=""></div></body></html>