[swift-evolution] Variadic generics discussion

Austin Zheng austinzheng at gmail.com
Mon May 30 13:41:43 CDT 2016


(more inline)

On Mon, May 30, 2016 at 6:30 AM, plx via swift-evolution <
swift-evolution at swift.org> wrote:

> A few more comments inlined now that I’ve read it.
>
> I like the `fold` construct but feel like for this use it needs a few more
> options-or-variants.
>
> The first one is some more-explicit support for “early exit”.
>
> EG for the cascading-lookup example, you can convert it to this `#fold`:
>
>   private func lookupKey(key: K) -> V? {
>     return #fold(
>       start: nil,
>       reducer: {
>         (table, value)
>         in
>         return value ?? table[key]
>       },
>       values: tables
>     )
>   }
>
> …but unless the compiler is very savvy you are going to have a bunch of
> unneeded stuff even after your “success” (it won’t do another lookup, but
> it just seems wasteful and could be wasteful in other contexts).
>
> Relatedly, that `#fold` is only going to work if the compiler can infer a
> proper type for the `table` argument to the `reducer`.
>
> If you have to specify the types explicitly via a helper function, it
> seems like this won’t work:
>
>   func lookupIfNecessary<T:LookupTable where T.Key == K, T.Value ==
> V>(table: T,  key: K, value: V?) -> V? {
>      return value ?? table[key]
>   }
>
> …b/c the signature isn’t right for the reducer, and it seems like this
> might work:
>
>   func createLookupFunction<T:LookupTable where T.Key == K, T.Value ==
> V>(key: K) -> (T, V?) -> V? {
>      return { (t,v) in return v ?? t[key] }
>   }
>

Yes, the fundamental problem here is that a user might need to pass an
arbitrary number of additional, non-pack-dependent arguments into the
reducer.

One possible solution might be an "AdditionalInfo" parameter representing
some constant always passed into the reducer function. This would
effectively do the same thing as a closure closing over some common state,
but maybe be a little less horrific to the user?

#fold(start: U, constantData: V, reducer: (#requirements, U, V) -> U,
values: T...)

(I ran into this issue some time ago as an app developer where I needed to
connect two APIs. One of them took fewer arguments than the other, and we
needed to preserve the additional context. The offending API was also
vended by a 3rd-party library, so it couldn't be changed. I remember an
elegant solution using multiple argument list currying, a feature which is
now gone from Swift...)


>
>   private func lookupKey(key: K) -> V? {
>     return #fold(
>       start: nil,
>       reducer: createLookupFunction(key), // <- should work…I hope...
>       values: tables
>     )
>   }
>
> …but it seems a bit convoluted; perhaps there’s a trick here? Or perhaps a
> variant with an adjusted signature like so:
>
>   // perhaps inout K ? or as an option?
>   #foldWithContext(context: K, start: U, reducer:(#requirements,U,K) -> U,
> values: (T…)) -> U
>
> Relatedly, having `#fold` variants like the above that include the current
> index could address a lot of the uses for integer-based indexing:
>
>   #indexedFold(start: U, reducer:(#requirements,U,Int) -> U, values: (T…))
> -> U
>
> …(the above can be done w/out dedicated support, but dedicated support
> might be more compiler-friendly on this one).
>

Here's a revised signature for #fold(), taking in account the things you
requested (which are good points, the Objective-C "BOOL*" arguments on
enumeration blocks and the .enumerate() method on Swift collections attest
to that):

#fold<...T, U>(start: U, reducer: (#requirements, Int, U) -> (U, Bool),
values: T...) -> U

The reducer takes in an Int which is provided as the index into the pack.
It returns a tuple, (U, Bool), which can be used to specify early exist.
There could be "sugared" versions of #fold which don't provide the index
and/or the early return functionality; those wouldn't be too difficult to
design.

(I didn't include the additionalInfo feature mentioned above, but it
wouldn't be too hard to munge it in.)


>
> Finally, after reading it over again I really find the `…` confusing
> beyond the initial declaration sites (as you can perhaps tell since I’ve
> been mis-using it in my replies).
>
> I can’t come up with an alternative that isn’t a major regression for
> simple declarations, but if a more-explicit syntax could be invented I’d
> highly prefer it.
>

The syntax is desperately in need of improvement. Even I had to read over
my examples a few times to make sure I was using everything correctly.

I'm thinking about a more tuple-oriented approach that would eliminate the
distinction between packs and tuples everywhere except when calling or
defining a generic function or defining a generic type, which might
eliminate some of the confusion.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160530/432f2648/attachment.html>


More information about the swift-evolution mailing list