> On Jan 11, 2018, at 11:42 PM, Brent Royal-Gordon <br...@architechies.com> 
> wrote:
> 
>> On Jan 11, 2018, at 4:21 PM, Paul Cantrell <cantr...@pobox.com 
>> <mailto:cantr...@pobox.com>> wrote:
>> 
>> This raises a question related to Chris’s: what is the utility of having 
>> Limb conform to a protocol instead of just providing allValues ad hoc? Does 
>> CaseEnumerable / ValueEnumerable serve any purpose other than triggering 
>> special behavior in the compiler? Would the protocol ever be used as the 
>> type of something in code?
>> 
>> My answers, admittedly weak ones, are: (1) conventions are nice and 
>> consistency is nice, and (2) you never know how an abstraction might be 
>> used, but you do know that people will be angry when it should fit but 
>> doesn’t. I can’t come up with a more compelling or specific argument than 
>> those.
> 
> Here's a place where you might want to use the protocol: Suppose you're 
> writing a table view data source that displays editable controls for a form. 
> You support several different types of controls, one of which is a list of 
> choices for a picker controller.
> 
>       enum Control<Value> {
>               case textField
>               case picker(choices: [Value])
>               …
>               
>               func makeView() -> UIView { … }
>               subscript(valueOf view: UIView) -> Value { get { … } set { … } }
>       }
> 
> Presumably you end up writing a schema which looks something like:
> 
>       formDataSource = FormDataSource(value: person)
>       formDataSource.fields = [
>               Section(title: nil, fields: [
>                       Field(title: "Name", keyPath: \.name, control: 
> .textField),
>                       Field(title: "Gender", keyPath: \.gender, control: . 
> picker(choices: Array(Gender.allValues))),
>                       …
>               ])
>       ]
>       tableView.dataSource = formDataSource
> 
> The `Array(Gender.allValues)` here is clutter; it'd be nice if we didn't have 
> to write it explicitly. After all, if you're choosing a value of something 
> you know is an enum, it's sensible to assume that you want to choose from all 
> values if it doesn't specify anything more specific. If `ValueEnumerable` is 
> a formalized protocol, you can do that with a constrained extension:
> 
>       extension Control where Value: ValueEnumerable {
>               static var picker: Control {
>                       return .picker(choices: Array(Value.allValues))
>               }
>       }
> 
> And now you just need to write:
> 
>                       Field(title: "Gender", keyPath: \.gender, control: . 
> picker)

One magic future day, the protocol could be Chris’s narrower CaseEnumerable, 
Brent’s hypothetical picker could use a more general ValueEnumerable, and we 
could short-circuit the whole debate with:

    extension CaseEnumerable: ValueEnumerable {
        var allValues: WhateverThisTypeIsSupposedToBe {
            return allCases
        }
    }

…but I recall a particularly thorny “alternatives considered” section about the 
implications of allowing extensions to add protocol conformance to other 
protocols!

Absent that language feature, I do think Brent has a point.

I tend to agree with Chris’s assessment here, which to my eyes doesn’t 
contradict Brent’s example:

> On Jan 12, 2018, at 1:28 AM, Chris Lattner <clatt...@nondot.org> wrote:
> 

> While we generally generally steer protocols towards being a “bag of 
> semantics” instead of a “bag of syntax”, there are definitely exceptions to 
> that rule, including the ExpressibleBy and other compiler intrinsic protocols 
> which really are *all about* defining syntax.  These protocols are not 
> particularly useful for generic algorithms.
> 
> The next level down are protocols like hashable/comparable that are useful 
> for generic algorithms, but are also particularly interesting because of 
> compiler synthesized conformances.  They are unique because they are both 
> sometimes interesting for generic algorithms, but also sometimes interesting 
> just because you want the synthesized members on *concrete* types for 
> non-generic uses.  IMO, this is the bucket that this proposal falls into.


That seems right: this CaseEnumerable / ValueEnumerable protocol will usually 
be about synthesized conformance, but occasionally be about semantics that are 
useful for generic algorithms.

I wouldn’t want to touch the “all possible values for all types” idea with an 
ℵ₀-foot pole.[1] However, I don’t see the harm in going Brent’s direction: a 
protocol whose “bag of semantics” is along the lines of “has a limited, known 
set of possible values that does not change during execution, and that might 
reasonably be iterated over or displayed to the user as a set of choices.”

That suggests to me the name ValueEnumerable. As with Equatable, it is named 
for the generic use, but synthesized by the compiler in a specific situation 
where it makes obvious sense to do so.

Cheers, P

[1] Surgeon general warning: cardinalities are not numbers. Do not use the 
Cantor hierarchy for measurement. Do not attempt to count to infinity. Do not 
shake hands with infinity. Do not approach an undomesticated infinity without 
approved protective gear. Stay safe out there.

> Basically, having `ValueEnumerable` be a formal protocol means we can extend 
> this small automatic behavior into larger automatic behaviors. I can imagine, 
> for instance, building a fuzzer which, given a list of `WritableKeyPath`s 
> whose types are all `ValueEnumerable`, automatically generates random 
> instances of a type and feeds them into a test function. If we get read-write 
> reflection, we might be able to do this automatically and recursively. That'd 
> be pretty cool.
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to