on Mon Jan 16 2017, Charles Srstka <cocoadev-AT-charlessoft.com> wrote:
> On Jan 16, 2017, at 6:42 PM, Dave Abrahams via swift-evolution > <[email protected]> wrote: >> >> >> on Mon Jan 16 2017, Charles Srstka <[email protected] >> <mailto:[email protected]>> wrote: >> > >>>> On Jan 16, 2017, at 7:49 AM, Chris Eidhof via swift-evolution >>>> <[email protected]> wrote: >>>> >>>> Hi, >>>> >>>> How does everyone feel about adding a second version of `reduce` to >>> >>>> `Sequence`? Instead of a `combine` function that's of type `(A, >>>> Element) -> A`, it would be `(inout A, Element) -> ()`. This way, we >>>> can write nice functionals algorithms, but have the benefits of >>>> inout (mutation within the function, and hopefully some copy >>>> eliminations). >>>> >>>> IIRC, Loïc Lecrenier first asked this on Twitter. I've been using it >>>> ever since, because it can really improve readability (the possible >>>> performance gain is nice, too). >>>> >>>> Here's `reduce` with an `inout` parameter, including a sample: >>>> https://gist.github.com/chriseidhof/fd3e9aa621569752d1b04230f92969d7 >>>> >>>> -- >>>> Chris Eidhof >>> >>> I did this in my own private code a while ago. There is one drawback, which >>> is that Swift’s type >>> inference system isn’t quite up to handling it. For example, doing this >>> results in an “ambiguous >>> reference to member” warning: >>> >>> range.reduce([Int]()) { $0.append($1) } >> >> The diagnostic could be better, but the compiler shouldn't let you do >> that, because it requires passing an unnamed temporary value ([Int]()) >> as inout. > > No it doesn’t. The signature of the method is: > > func reduce<A>(_ initial: A, combine: (inout A, Iterator.Element) -> ()) -> A > > The unnamed temporary value is “initial” here, which is not passed as inout; > the inout parameter is > the first argument to the “combine” closure. The value represented by the > ‘initial’ parameter is > passed to the closure, true, but only after being stored in a not-unnamed > ‘var’ variable, as you can > see from the source of the proposed method: > > func reduce<A>(_ initial: A, combine: (inout A, Iterator.Element) -> ()) -> A > { > var result = initial > for element in self { > combine(&result, element) > } > return result > } > > Therefore, I don’t understand this objection. > >>> One would think that the type of this closure should be clear: >>> >>> 1) The closure calls append(), a mutating function, so $0 must be inout. >>> >>> 2) The closure doesn’t return anything, which should rule out the >>> default implementations of reduce, >> >> The closure *does* return something: (), the empty tuple > > But it’s not what it’s supposed to return. Sequence’s implementation > of reduce, which the compiler thinks matches the above, is declared > like this: > > public func reduce<Result>(_ initialResult: Result, _ > nextPartialResult: (Result, Self.Iterator.Element) throws -> Result) > rethrows -> Result > > The closure is supposed to return Result, which in this case would be > [Int]. It doesn’t, so I’m not sure why the compiler is thinking this > is a match. Okay, sounds like I'm totally wrong! Has to happen at least once in a lifetime, doesn't it? 😉 So please file bug reports for these issues. -- -Dave _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
