> On Jan 10, 2018, at 4:21 PM, Chris Lattner <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
https://lists.swift.org/mailman/listinfo/swift-evolution