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.

Charles

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to