# \`findmax\` and friends: confusing behaviour to be introduced in 1.7

**URL:** <https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904>\
**Category:** Internals & Design\
**Created:** [May 27, 2021, 7:23am UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904 "2021-05-27T07:23:32Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [May 27, 2021, 2:11pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/21 "2021-05-27T14:11:39Z")

</div>

It works, but it is not very valuable, right? I mean, main objection was that `findmax(f, itr)` should work with any iterator, even those which has no index.

With proposed change key feature of the current version of `findmax(f, itr)` is nullified (triplet will work only for arrays) and the only addition is `v[ix]` which is not that important. I mean, it literally just remove one line from

```julia
val, ix = findmax(f, A)
domain = A[ix]

```

Also, three values returned as a tuple is very confusing 🙂 I already do not remember which one is index, which is `A[ix]` and `f(A[ix])`.

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [May 27, 2021, 2:21pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/22 "2021-05-27T14:21:44Z")

</div>

This is a different issue (but stil, a gotcha). In Julia vectors, pairs, and other iterables are ranked lexicographically. so `<` is defined for pairs, but of course `sqrt` is not.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 27, 2021, 2:26pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/23 "2021-05-27T14:26:20Z")

</div>

I believe this is directly related to the confusing nightly `findmax(f, A)` behavior. Original `findmax(Dict)` doesn’t apply any function to pairs:

```julia
julia> findmax(Dict(2 => 3, 4 => 5))
(5, 4) # value, key

```

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [May 27, 2021, 2:26pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/24 "2021-05-27T14:26:44Z")

</div>

Oh I see. Yes that is awkward I guess.

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [May 27, 2021, 2:56pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/25 "2021-05-27T14:56:12Z")

</div>

> [@Skoffer](#):
>
> it’s better to introduce another function name for current version of `findmax(f, v)` instead

This is what I would suggest also. Since we have a contract for `findmax(v)` that we cannot change till 2.0 release (like it or not) I think we should follow the same contract in `findmax(f, v)` and a function that works for any iterable needs a new name.

---

<div class="post-metadata">

**Author:** ![ColinCaine](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colincaine/32/7211_2.png) [@ColinCaine](https://discourse.julialang.org/u/ColinCaine)\
**Post date:** [May 27, 2021, 3:06pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/26 "2021-05-27T15:06:26Z")

</div>

I wrote the PR that added these methods (with help from tkf and others). The implemented behaviour is as intended.

We acknowledged in the PR that `findmax(identity, itr) != findmax(itr)` is a bit confusing, but the docstrings do accurately describe the behaviour. A previous iteration of the docstrings also gave the rationale for `findmax(xs)` being different: when `xs` is an object that has a domain and codomain then you don’t need to give a function.

Personally, I think the new methods make a lot of sense and `findmax([1, 2, 3])` also makes sense to me, but y’know, if everyone hates it we can do something else.

Also, the behaviour with `findmax(Dict(2 => 3, 4 => 5))` is the same in 1.6 and 1.7. Any breaking change in behaviour of `findmax(itr)` between versions is unintentional (please let me know/open an issue). It is expected that the 1.7 implementation works with some values that would error in 1.6, but that’s intentional.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 27, 2021, 3:17pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/27 "2021-05-27T15:17:36Z")

</div>

> [@ColinCaine](#):
>
> when `xs` is an object that has a domain and codomain then you don’t need to give a function

What exactly do you mean here?  
If this is about arrays and other indexable collections, then it is not really expected to treat them as functions. Unlike matlab, julia doesn’t consider indexing arrays and calling functions as operations of the same kind.

> [@ColinCaine](#):
>
> We acknowledged in the PR that `findmax(identity, itr) != findmax(itr)` is a bit confusing, but the docstrings do accurately describe the behaviour.

I believe that a wider context was not evaluated enough in that design.  
It would be a different story if this was the only case of a reduction that takes an array and a function. However, there are lots of these in julia already, and they all do roughly `r(f, A) == r(map(f, A))`.  
Discussion here, and numerous questions in slack (I myself saw ~4, and this is for nightlies!), are additional evidence for this being confusing.

---

<div class="post-metadata">

**Author:** ![gbaraldi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gbaraldi/32/22101_2.png) [@gbaraldi](https://discourse.julialang.org/u/gbaraldi)\
**Post date:** [May 27, 2021, 3:21pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/28 "2021-05-27T15:21:17Z")

</div>

While the rationale behind the decision makes sense. From the point of view of the user it’s still very confusing. Most functions that work similarly to `findmax(itr)` i.e mean, sum work the same when calling `sum(identity,itr) ` so for consistency I would argue that `findmax(identity,itr) == findmax(itr)`. So maybe creating two separate functions might be the best solution.

---

<div class="post-metadata">

**Author:** ![bkamins](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bkamins/32/208538_2.png) [@bkamins](https://discourse.julialang.org/u/bkamins)\
**Post date:** [May 27, 2021, 3:27pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/29 "2021-05-27T15:27:55Z")

</div>

> [@gbaraldi](#):
>
> While the rationale behind the decision makes sense. From the point of view of the user it’s still very confusing.

This is also my point. That is why I have said that it is an “unfortunate case” above. I have spent many days on working out similar dilemmas in DataFrames.jl. Let me give an example (just to show I understand the pain). We had `join` function in DataFrames.jl that was well documented. However, it did something else than `join` in Julia Base. There was really a very low risk of confusion. Still we decided to remove it and create new function names (if I recall correctly @kristoffer.carlsson suggested this).

I know that you have spent a lot of time discussing pros and cons of `findmax(fun, itr)` and it is a reasonable design. What I (and probably other users here) want to voice that we are afraid that it will be hard to learn/use/teach this design even if it makes sense and is a best possible solution from an advanced user’s perspective. Of course Julia core dev team probably should weigh pros and cons and make a decision.

---

<div class="post-metadata">

**Author:** ![tbeason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tbeason/32/15898_2.png) [@tbeason](https://discourse.julialang.org/u/tbeason)\
**Post date:** [May 27, 2021, 3:38pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/30 "2021-05-27T15:38:39Z")

</div>

I missed this discussion on GH.

I think the new behavior seems a lot more like a proper `argmax` – you get both the max `f(x)` and `x`. To me, this is how an `argmax` works. That being said, that is NOT what I would expect a `findmax` function to do. In that case, I’d expect the maximum `f(x)` and its index. So, my thoughts would be to drop the `argmax(x)` methods and replace them with this new behavior, and leave `findmax(f,a)` as others have already said.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 27, 2021, 3:41pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/31 "2021-05-27T15:41:48Z")

</div>

It would indeed be great to have concepts of `mathematical argmax` and `indmax (currently called argmax)` separated. However, what you suggest is breaking, and cannot happen in julia 1.x.

---

<div class="post-metadata">

**Author:** ![ColinCaine](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colincaine/32/7211_2.png) [@ColinCaine](https://discourse.julialang.org/u/ColinCaine)\
**Post date:** [May 27, 2021, 3:54pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/32 "2021-05-27T15:54:12Z")

</div>

Yeah, I do get how it’s different from e.g. `sum(f, A)`, and that was the context we were evaluating.

It is obviously legit to be querying this design decision, that’s why it’s in nightly, after all.

@aplavin, yes the intended interpretation is that indexable collections are maps from keys to values, in the same way that a function maps from inputs to outputs.

It’s true that that’s not how we think of collections in Julia, but it is consistent with how functions and collections are sometimes described in CS and maths and it gives one consistent meaning to this family of functions (they always do something with the domain and codomain, and if you don’t provide a function then the domain and codomain are the implicit ones for that object). You can then imagine defining your own methods for e.g. `findmax(x::DifferentialEquationModel)` or whatever.

It might be better if `argmax(xs)` didn’t have its current definition, but we just have to live with it.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [May 27, 2021, 3:56pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/33 "2021-05-27T15:56:15Z")

</div>

> [@ColinCaine](#):
>
> yes the intended interpretation is that indexable collections are maps from keys to values, in the same way that a function maps from inputs to outputs.

Hm. In that interpretation, two argument `findmax`/`findmin` over dicts is broken then, since it (by that interpretation) _should_ iterate over keys, and not the key-value tuple the nightly version currently does (unlike the single argument version, which does the correct thing).

---

<div class="post-metadata">

**Author:** ![ColinCaine](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colincaine/32/7211_2.png) [@ColinCaine](https://discourse.julialang.org/u/ColinCaine)\
**Post date:** [May 27, 2021, 3:59pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/34 "2021-05-27T15:59:40Z")

</div>

I don’t think you’re right. Could you give an example input and what the current implementation does and what you would expect?

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [May 27, 2021, 4:01pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/35 "2021-05-27T16:01:06Z")

</div>

Sure, I’ll just copy & paste what I posted above:

```julia
julia> versioninfo()                           
Julia Version 1.7.0-DEV.1070                   
Commit 69733789ac (2021-05-05 07:08 UTC)       
Platform Info:                                 
  OS: Linux (x86_64-linux-gnu)                 
  CPU: Intel(R) Core(TM) i7-6600U CPU @ 2.60GHz
  WORD_SIZE: 64                                
  LIBM: libopenlibm                            
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)       
Environment:                                   
  JULIA_PKG_SERVER =                           
  JULIA_NUM_THREADS = 4                        

julia> findmin(Dict(2 => 3, 4 => 5)) # good so far
(3, 2)                              

julia> findmin(identity, Dict(2 => 3, 4 => 5)) # I'd expect (3, 2) again, like above        
(2 => 3, 2 => 3)                                                 
                                                                 
julia> findmin(sqrt, Dict(2 => 3, 4 => 5)) # I'd expect (9, 2)
ERROR: MethodError: no method matching sqrt(::Pair{Int64, Int64})
Closest candidates are: ...

```

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 27, 2021, 4:01pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/36 "2021-05-27T16:01:49Z")

</div>

> [@ColinCaine](#):
>
> @aplavin, yes the intended interpretation is that indexable collections are maps from keys to values, in the same way that a function maps from inputs to outputs.

Is there any other place in julia where arrays and functions are treated equivalently? I see that they are considered fundamentally different everywhere _(and this is a good thing!)_:

```julia
julia> A = [1, 10, 100];

julia> A[2]
10
julia> A(2)
ERROR: MethodError: objects of type Vector{Int64} are not callable

julia> map(x -> A[x], 1:3)
3-element Vector{Int64}:
   1
  10
 100
julia> map(A, 1:3)
ERROR: MethodError: objects of type Vector{Int64} are not callable

julia> sum(x -> A[x], 1:3)
111
julia> sum(A, 1:3)
ERROR: MethodError: objects of type Vector{Int64} are not callable

# etc

```

---

<div class="post-metadata">

**Author:** ![darsnack](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/darsnack/32/10144_2.png) [@darsnack](https://discourse.julialang.org/u/darsnack)\
**Post date:** [May 27, 2021, 4:02pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/37 "2021-05-27T16:02:50Z")

</div>

If we assume the interpretation @ColinCaine gave, then `findmax(f, ::Dict)` implies the domain is pairs just as in `findmax(f, ::AbstractArray)`, the domain is values of the array.

The heart of the problem is that under that interpretation, `f` is implicitly `i -> A[i]` and `k -> d[k]` in the array and dict variants for a single argument. Using `identity` is not equivalent in this interpretation.

I just wanted to clarify; I don’t have a horse in this race (yet).

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [May 27, 2021, 4:03pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/38 "2021-05-27T16:03:38Z")

</div>

I know that this is the difference. I think assuming tuples as the domain is the wrong choice here, that’s all, since Dicts are very much “indexable” in the sense that they have `getindex` defined. They’re just not a rectangular data store.

I guess what I’m saying is that I think `findmax` and friends should (for indexable collections) call `keys(coll)`, index with them (in order returned by `keys`) into the collection, pass whatever they get to the supplied `f` and use the result for max/minimality, returning the tuple `(f(col[idx]), idx)`. I don’t think this is solvable for indexable and non-indexable collections/iterables in one swoop.

---

<div class="post-metadata">

**Author:** ![ColinCaine](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/colincaine/32/7211_2.png) [@ColinCaine](https://discourse.julialang.org/u/ColinCaine)\
**Post date:** [May 27, 2021, 4:14pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/39 "2021-05-27T16:14:49Z")

</div>

Cool, I think we both understand what the other wants and we just disagree on what the method should do?

In case there’s any doubt (and because I wrote this before you wrote your next reply):

The distinction we made in the original PR is that we only imply the domain and codomain when a function has not been given.

If a function has been given then the domain is the iterable and the codomain is the outputs of the function for the values of the iterable.

This gives consistent behaviour for Dicts, Arrays and any other indexable collection, and gives a useful result for non-indexable collections when you provide `f`. And I’ll emphasise that I think `findmax` on non-indexable collections is a useful thing to have.

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [May 27, 2021, 4:27pm UTC](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904/40 "2021-05-27T16:27:07Z")

</div>

I do not think, that anyone is saying that nightly `findmax` is useless or bad some other way. It’s just inconsistent with other Julia ecosystem. Name it `findmaxvalue` or something like this and it will be used in many applications, no doubt.

[Previous page](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904.md?page=1)

[Next page](https://discourse.julialang.org/t/findmax-and-friends-confusing-behaviour-to-be-introduced-in-1-7/61904.md?page=3)
