-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Description
Consider a Julia neophyte trying to sort Char
s:
julia> maximum('A', 'B')
ERROR: MethodError: objects of type Char are not callable
Stacktrace:
[1] mapreduce_first(::Char, ::Function, ::Char) at ./reduce.jl:293
[2] mapfoldl_impl(::Char, ::Function, ::NamedTuple{(),Tuple{}}, ::Char) at ./reduce.jl:60
[3] #mapfoldl#186(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(mapfoldl), ::Char, ::Function, ::Char) at ./reduce.jl:72
[4] mapfoldl(::Char, ::Function, ::Char) at ./reduce.jl:72
[5] #mapreduce#194(::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}, ::typeof(mapreduce), ::Char, ::Function, ::Char) at ./reduce.jl:200
[6] mapreduce(::Char, ::Function, ::Char) at ./reduce.jl:200
[7] maximum(::Char, ::Char) at ./reduce.jl:524
[8] top-level scope at none:0
Of course they should have used max
. But that stack trace is long and confusing, considering that already the very first call maximum(::Char, ::Char)
, the types are all wrong for maximum
. Normally, Julia methods have a signature that takes only appropritate abstract types, like Number
and so on, precisely so that the method only apply to inputs of the correct types.
The problem is that there are no types for Callable
or Iterable
, so the signature of maximum
is totally untyped: maximum(f, a) = mapreduce(f, max, a)
.
To solve this, I suggest implementing Holy traits Iterable
and Callable
:
maximum(f, itr) = maximum(f, callable(f), itr, iterable(itr))
maximum(f, ::Callable, itr, ::Iterable)
One argument against doing this is that it could open the door to all kinds of traits in Base for this or that property. However, in my experience, it seems like the properties "iterable" and "callable" are particularly often used, just consider the amount of confusion about what is iterable or not we often see.