[swift-evolution] [Pitch] KeyPath based map, flatMap, filter
Dave Abrahams
dabrahams at apple.com
Mon Jul 10 13:01:53 CDT 2017
on Sun Jul 09 2017, Brent Royal-Gordon <brent-AT-architechies.com> wrote:
> But however we achieve it, I think a spoonful of
> syntactic sugar would help the medicine go down.
Let me be clear: syntactic sugar matters. Otherwise, we'd all be
programming directly in LLVM IR. It's just a question of what you have
to pay to get it.
>> By the way, if you're worried about whether subtyping will fly, I've
>> recently been thinking there might be a role for a “promotion” operator
>> that enables lossless “almost-implicit” conversions, e.g.:
>>
>> someNumber^ is equivalent to numericCast(someNumber)
>> \.someKeyPath^ is equivalent to { $0\.someKeyPath }
>> someSubstring^ is equivalent to String(someSubstring)
>>
>> etc.
>
> I actually played with something like this years ago (pre-open source,
> IIRC), but I used `^` as a prefix operator and made it support only
> widening conversions. But it was old code, and redoing it nerd-sniped
> me so hard that I kind of ended up making a whole GitHub project from
> it: <https://github.com/brentdax/Upconvert>
>
> The main component is an `Upconvertible` protocol which encapsulates
> the conversion. That works really well in some ways, but it also
> creates some important limitations:
>
> 1. I had trouble incorporating downconversions in a reasonable
> way. Key paths in particular would require either compiler support or
> some really hacky, fragile code that opened up the closure context and
> pulled out the KeyPath object.
>
> 2. There's no good way to support more than one upconversion from a
> single type. (For instance, you can't make `UInt16` upconvert to both
> `Uint32` and `Int32`.)
>
> 3. Even if #2 were somehow fixed, you still can't make all
> `LosslessStringConvertible` types conform to `Upconvertible`.
>
> 4. Can't upconvert from a structural type, of course.
AFAICT, all of the above come down to having tried to build this idiom
around a protocol with an associated type. I think of it as a
special-case syntactic shortcut for “value-preserving conversion to
deduced type.” Just overload the operator and be done with it. Maybe
protocols like BinaryInteger should have a generic operator, but this
doesn't deserve a protocol of its own.
> 5. I wanted to support passing through any number of valid
> upconversions with a single `^` operator, but the only way I could
> find to do that was to overload the operator with a two-step version,
> a three-step version, etc.
>
> 6. Upconverting a `\.keyPath` expression caused an ambiguity error; I
> had to overload the operator to make it favor `KeyPath`. (Workaround
> code:
> https://github.com/brentdax/Upconvert/blob/master/Upconvert/Conformances/KeyPath.swift#L25)
Meh; that's a fact of life when overloading.
> Several-to-all of these could be avoided with a built-in language feature.
IMO no language feature is needed for this.
> As for the ergonomics…well, `people.map(^\.name)` definitely feels
> better than the closure alternative. But it's something you have to
> learn is possible, and even if you knew about `^` in the context of
> (say) numeric conversions, I'm not sure people would think to try it
> there. It basically means you need to know about three slightly
> esoteric features instead of two; I'm not sure people will discover
> that.
Yes, but there's a trade-off between discoverability, and introducing
more implicit conversions, which will slow down the type checker and can
make errors harder to understand. Note: I'm not arguing for either
approach in particular. They're just available alternatives.
--
-Dave
More information about the swift-evolution
mailing list