Proposal to make functions first class citizens in context of multiple dispatch


#1

When calling a function, the function itself is treated like a 2nd class citizen.
That is, the function is fixed and the dispatch system only considers the other arguments.

However, when broadcasting the function is treated on par with the other arguments.

[1,2] .+ [3,4]
# becomes
Base.broadcast(.:+, [1,2], [3,4])

I propose a method dispatch mechanism that first dispatches to a function that can be overloaded, much like the getproperty function has been introduced to overload calls to getfield.

The name invoke is already taken. Maybe the function name call would be suitable.

The ability to define functions like
Base.call(f, x::MyNumberType, args...) or Base.call(f, A::MyCollection, args...)
could make it a lot easier to wrap numbers or collections with a new type.
Currently one may have to define over a hundred new methods just to wrap number types.
See package AbstractNumbers for an example.

With the proposed mechanims one could accommodate many functions with only a handfull of “call” overload methods, one for each method signature.

I am aware of Proposal for a first-class dispatch wrapper but that involves a much more complicated mechanism.

Feedback please.


#2

If you want to define a hierarchy of function types, you can already do this by defining your own “functor” types. Julia already has “call” overloading that treats the caller on the same footing as the arguments. It’s not clear what your proposal adds to this. (The other proposal you linked allows dispatch on the return type, which would be new.)


#3

Could you give an example of how to define my own hierarchy of function types and my own “functor” types?


#4

This seems similar to the apply function that @StefanKarpinski suggested in this issue from 2014: https://github.com/JuliaLang/julia/issues/5561

I think it would be great if something like that could work.


#5

The problem with callable types is that One cannot exploit type hierarchies. My understanding is that one cannot define a „call“ on an abstract type? I think that happened in the v0.5 transition? One can get around it with macros, but it is a bit of work


#6

For F:C\rightarrow D to qualify as a functor it has to satisfy 2 axioms

  • For any composable pair f,g\in C it must be that Fg\cdot Ff = F(g\cdot f)
  • For earch c\in C it must be that F(1_c) = 1_{Fc}

Just wanted to point this out, because the term functor is not arbitray, but has a very specific meaning, and I don’t really see how it applies in this context.


#7

That’s only one meaning. The word has been used with a variety of different meanings in mathematics and computer science.