Maximum does not work on UInt

a = convert(UInt, 1)
b= convert(UInt, 2)
maximum(a, b)

fails with

ERROR: MethodError: objects of type UInt64 are not callable
Maybe you forgot to use an operator such as *, ^, %, / etc. ?
Stacktrace:
 [1] mapreduce_first(f::UInt64, op::Function, x::UInt64)
   @ Base ./reduce.jl:424
 [2] mapreduce(f::UInt64, op::Function, a::UInt64)
   @ Base ./reduce.jl:451
 [3] maximum(f::UInt64, a::UInt64; kw::@Kwargs{})
   @ Base ./reduce.jl:705
 [4] top-level scope
   @ REPL[8]:1

I could have sworn that used to work. Is there a rationale for this (new) behavior?

Julia Version 1.10.0
Commit 3120989f39 (2023-12-25 18:01 UTC)
Build Info:

    Note: This is an unofficial build, please report bugs to the project
    responsible for this build and not to the Julia project unless you can
    reproduce the issue using official builds available at https://julialang.org/downloads

Platform Info:
  OS: macOS (arm64-apple-darwin22.6.0)
  CPU: 10 × Apple M1 Max
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, apple-m1)
  Threads: 1 on 8 virtual cores
Environment:
  JULIA_PKG_DEVDIR = [...]

this is the contract of the functioon maximum

help?> maximum
search: maximum maximum!

  maximum(f, itr; [init])

  Return the largest result of calling function f on each element
  of itr.

  The value returned for empty itr can be specified by init. It
  must be a neutral element for max (i.e. which is less than or
  equal to any other element) as it is unspecified whether init is
  used for non-empty collections.

your code is equivalent to


julia> a(b)
ERROR: MethodError: objects of type UInt64 are not callable
Maybe you forgot to use an operator such as *, ^, %, / etc. ?        
Stacktrace:
 [1] top-level scope
   @ REPL[5]:1

perhaps you wouls use max(a,b)

1 Like

Yes it does. I’m just perplexed that a) it used to work and b) it’s not super intuitive that maximum is not the maximum element of an iterable, but instead the maximum reduction over a mapping

which version? it throws the same error as far back as 1.0, so must be older than that

Is it just that you meant max(a,b) instead of maximum(a,b)?

1 Like

It looks like a regression error on my part. I was on 1.9.x when I last ran this code in late Nov and in early Dec I introduced maximum and none of my tests caught it.

I’ve seen this mistake (confounding maximum / minimum with max / min in either way) various times. I think it’s quite a reasonable confusion, so perhaps it would be useful to extend the error message of those functions, with hints such as: Maybe you meant `max`?, etc.

What I’m not sure of is whether it is easy to generalize the sets of input arguments for which such a hint really makes sense. Or might it be simply given whenever there is a MethodError with those functions?

5 Likes

It is the maximum of an iterable, though?

julia> it = rand(10);
julia> maximum(it)
0.910222299638805

The “apply a function” part is optional. You can think of the default map being the identity if you wish.

What you did was giving multiple elements to maximum not an iterable. For more than two arguments you’d get the MethodError one would expect

julia> maximum(1,2,3)
ERROR: MethodError: no method matching maximum(::Int64, ::Int64, ::Int64)
...

However for exactly 2 arguments you end up with the method that finds the maximum of an iterable after application of a function. So it tried to apply 1 to 2 which gave you the MethodError you saw. Part of that story is also the circumstance that numbers are iterable (so happenend was more like 1(2[1]). Otherwise the error could have been something like “2 is not iterable and maximum expects an iterable”, which maybe would have been more helpful in this specfic case. This is also what makes the suggestion of @heliosdrm difficult I think.

Maybe one could add a method like:

function maximum(too, many, args...)
    error("`maximum([f ,] itr)` got too many arguments. For finding the maximum of several elements use `max`.")
end

That still has the unfortunate case of exactly 2 arguments though.

EDIT: Better than a method would probably be setting an error hint via Base.Experimental.register_error_hint but that cannot quite capture the 2-arg case either… As for generalizing: Probably good enough would the case maximum(::Number, ::Number).