Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prod() over zero-length tuple generator should just return 1? #40160

Closed
davidavdav opened this issue Mar 23, 2021 · 5 comments
Closed

prod() over zero-length tuple generator should just return 1? #40160

davidavdav opened this issue Mar 23, 2021 · 5 comments
Labels
fold sum, maximum, reduce, foldl, etc.

Comments

@davidavdav
Copy link
Contributor

Hello,

This might be related to #28028, #36262 and similar issues.

Observing the following statements:

## These all work as expected
julia> collect(i for i in Int[]) == Int[]
true
julia> sum(i for i in Int[]) == 0
true
julia> prod(i for i in Int[]) == 1
true
julia> collect(i for (i, j) in zip(Int[], Int[])) == Int[]
true
## but here I would expect result `1`
julia> prod(i for (i, j) in zip(Int[], Int[]))
ERROR: ArgumentError: reducing over an empty collection is not allowed
...

I can't really say where this goes wrong, whether it is in the zip generator or in the reduction in prod().

The following "identity" function id(n) evaluates for all integers except 1:

import Primes
id(n::Integer) = prod(prime^power for (prime, power) in Primes.factor(n))

Of course such functions could check for cases where n==1 but that is somewhat clumsy, I think. My actual use is in a function computing the sum of the divisors of a positive natural number:

sigma(n::Integer) = prod((prime ^ (power + 1 ) - 1 ) ÷ (prime - 1) for (prime, power) in Primes.factor(n))
sigma(28) == 2 * 28
sigma(1)
ERROR: ArgumentError: reducing over an empty collection is not allowed
@dkarrasch dkarrasch added the fold sum, maximum, reduce, foldl, etc. label Mar 24, 2021
@JeffBezanson
Copy link
Member

The difficulty with this is that without any elements, we don't know what the domain is, so we don't know if 1 is the correct identity element.

(We have prod(()) == 1 but it's a legacy thing that's not ideal.)

@davidavdav
Copy link
Contributor Author

Is prod(()) what prod(i for i in Int[]) in the end evaluates to? I can imagine it is difficult to deduce the type of the argument from an empty generator expression, but julia is still able to do this for the non-tuple cases

prod(i for i in Int[]) === 1
prod(i for i in Float64[]) === 1.0

but tuples start to make a difference between collect and prod

collect(i for (i,j) in NTuple{2, Int}[]) == Int[]
collect(j for (i,j) in Tuple{Int, Float64}[]) == Float64[]
prod(i for (i,j) in NTuple{2, Int}[]) ## Error

Thanks, anyways, I can definitely live with the current behavior, I just wondered if this is intended or just hard to solve case.

@StefanKarpinski
Copy link
Member

For products and sums, I feel like using true and false as "weak" identities would be ok, but it's a fairly big decision. And even if it works computationally, people will be surprised and complain about it, so 🤷🏻‍♂️

@JeffBezanson
Copy link
Member

Is prod(()) what prod(i for i in Int[]) in the end evaluates to?

Not exactly, we just have some special cases for the identity function, allowing i for i in ... to work.

@vtjnash
Copy link
Member

vtjnash commented Nov 23, 2021

similar to #36262 we have init now for this. See also triage's rationale at #29919 (comment) for why this is not implemented

@vtjnash vtjnash closed this as completed Nov 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fold sum, maximum, reduce, foldl, etc.
Projects
None yet
Development

No branches or pull requests

5 participants