User-declared function doesn't support overload

This doesn’t work

functions {
  real foo(real[] y0);
  real foo(real[,] y0);
}

It seems odd that Stan doesn’t allow user function overloading, since it’s already checking function signatures.

@yizhang There may already be an issue for that. If not, please open one on stan-dev/stan.

I know how to code it, I just don’t have the time to do all this stuff. What needs to happen is that the code generator needs to generate a single functor for each overloaded function. As is, the generator generates each function definition and matching functor. But you can’t have overloaded functors in C++, so all the versions need to go inside a single functor.

For the user-declared functions, we only generate forward declaration of functions. Maybe it’s easier for this case?

Searching “overload” doesn’t show similar issues. I’m creating one here.

Thanks! @mitzimorris may tackle this one sooner rather than later. Or I might find some time to do it. It shouldn’t be that hard.

Can we generate a generalized lambda function instead of a functor?

Maybe that’d be easier. Suppose have a function foo defined in Stan with type (real, real):real. The generator will produce something like this:

template <typename T1, T2>
typename promote_args<T1, T2>::type
foo(const T1& x1, const T2& x2) {
  ...
}

which can be used for ordinary function applications to real arguments in a Stan program. To use it as a higher-order function argument, we also use

class foo_fun {
  template <typename T1, T2>
  typename promote_args<T1, T2>::type
  operator()(const T1& x1, const T2& x2) const {
    return foo(x1, x2);
  }
};

The problem is that if we also have a foo defined of type (real):real, then we generate two functors and get a name conflict. All that needs to happen is that we need to delay generating until we can put both operator() overloads in a single functor. It’s not going to be too hard, but it’ll be the first time we couldn’t do basically local generation, so I’d be eager to use something a little more direct.

It wouldn’t be possible to statically define two lambda abstractions foo_fun, because there’d also be an ambiguity, e.g.,

auto foo = ... abstract first version of foo ...
auto foo = ... abstract second version of foo ...

Were you thinking we could use anonymous abstractions? Something like

ode_integrate((const auto& x1, const auto& x2).foo(x1, x2), ...)

I’m just not sure that’ll play nicely with an overloaded foo in all situations. We may need to replace those auto with the actual types.

Or were you thinking of some other approach?

Yes. Not sure it would work either.