Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

add type-unsafe 'invokeCallable' function to language module #5806

Open
jvasileff opened this issue Dec 8, 2015 · 11 comments
Open

add type-unsafe 'invokeCallable' function to language module #5806

jvasileff opened this issue Dec 8, 2015 · 11 comments

Comments

@jvasileff
Copy link
Contributor

There appears to be no way to perform invocations on Callables in a type-unsafe manner (that is, without at least having a properly typed Tuple to use as arguments) as you can with FuncitonDeclarations using invoke().

Given that correctly typed Tuples are not easy to create using the metamodel, the lack of an invoke method for Callables makes certain meta programming tasks difficult or impossible (see @renatoathaydes SO question).

Therefore, I propose a function ceylon.language.meta::invokeCallable() with the following signature be added:

shared native throws(`class IncompatibleTypeException`)
Anything invokeCallable(
        Anything(*Nothing) callable,
        Anything[] arguments);
@FroMage
Copy link
Contributor

FroMage commented Dec 8, 2015

Sounds like a legit use-case to me.

@renatoathaydes
Copy link

That would be helpful, I agree!

@tombentley
Copy link

  • I wonder if this would be better with variadic arguments
  • It is proposed that this be in ceylon.language.meta, but it doesn't feel terrible meta to me
  • It's a shame IncompatibleTypeException is from ceylon.language.meta.model, if we're reusing it here

@tombentley
Copy link

And:

  • surely invoke would be sufficient (or untypesafeInvoke or invokeDangerously), but invokeCallable seems tautologous to me: What else could you invoke than a Callable?
  • there's an ambiguity if the callable itself throws IncompatibleTypeException. We'd need a WrappedException to disambiguate. Do we want such a thing?

@gavinking
Copy link
Contributor

Given that correctly typed Tuples are not easy to create using the metamodel,

I don't immediately see what you mean by this. If it's true, then shouldn't we be addressing that problem directly?

@jvasileff
Copy link
Contributor Author

I don't immediately see what you mean by this. If it's true, then shouldn't we be addressing that problem directly?

Well, the idea of a Tuple<Anything, Anything, Anything[]> canonicalTuple(Anything[]) function did cross my mind, but for unsafe invocation of callables:

  • You would still need to assert the result to the Arguments type required by the callable, so the proposed invoke(c,args) is more flexible.
  • invoke() should be at least slightly more performant
  • While a canonicalTuple() seems like it may be more generally useful, I haven't convinced myself of that to point of being able to propose such a function. You still need to know the static Tuple type to put it to use.

@jvasileff
Copy link
Contributor Author

Of course, if canonicalTuple already existed, I would not have opened this request.

@renatoathaydes
Copy link

If this helps, the use case I had for this problem was to be able to pass arguments to a function when the types of both function and arguments were not known statically, but known to be "correct" at runtime... this allows me to implement a property-based test where function input is generated automatically.

I can definitely see how frameworks would need to do this kind of thing in other scenarios as well...

See how I use this here:

https://github.com/renatoathaydes/specks/blob/ee7f8473ca9e0176f2db0ce82400300628c3be63/source/com/athaydes/specks/specks.ceylon#L254

tombentley added a commit that referenced this issue Jan 8, 2016
tombentley added a commit that referenced this issue Jan 11, 2016
@FroMage FroMage modified the milestones: 1.2.1, 1.2.2 Jan 11, 2016
@FroMage FroMage modified the milestones: 1.2.2, 1.2.3 Feb 18, 2016
@quintesse
Copy link
Contributor

@tombentley I see you added some commit related to this. Any chance of getting this done for 1.2.3 or do we move it to 1.3?

@gavinking gavinking modified the milestones: 1.3, 1.2.3 Aug 25, 2016
@jvasileff
Copy link
Contributor Author

jvasileff commented Feb 3, 2017

An interim workaround, albeit a very slow one, which can be called like:

invokeCallable(Integer.string, [50]);
invokeCallable(plus<Integer>, {50, 25});

is:

shared Anything invokeCallable(
        Anything(*Nothing) callable, {Anything*} arguments)
    =>  `function invokeCallableHelper`.invoke {
            typeArguments = arguments.collect(type).reversed.fold<Type<Anything>[2]>
                    ([`Nothing`, `[]`])(([elements, rest], element)
                =>  let (union = elements.union(element))
                    [union, `class Tuple`.apply<>(union, element, rest)])[1:1];
            arguments = [
                callable,
                // relies on undocument property that Tuples are typed
                // by their contents
                arguments.fold<Anything[]>([])(uncurry(
                    Sequential<Anything>.withTrailing<Anything>))];
        };

Anything invokeCallableHelper<Arguments>
        (Anything(*Arguments) f, Arguments arguments)
        given Arguments satisfies Anything[]
    =>  f(*arguments);

@jvasileff
Copy link
Contributor Author

#7019 suggest the addition of something like the canonicalTuple() discussed above. That would help with this issue, but not entirely address it, since:

  1. It would needlessly expand trailing ranges in some cases, for example, with an argument list like ["", *(1:runtime.maxIntegerValue)] for a callable of type Anything(String, *Integer), and
  2. There's currently no fast and reliable way to get the Arguments runtime type-arg from a Callable<Anything, Nothing>

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants