5

I've got a generic function that needs to create a Tuple to call a function whose arguments I don't know the types of.

Something like this (except array in this example is created by some external code, so I can't just apply the function directly):

Result apply<Result, Where>(
    Anything[] array, 
    Callable<Result, Where> fun)
        given Where satisfies Anything[] => nothing;

Is there a type-safe way to implement this method and get the function to be called with the given arguments?

Renato
  • 12,940
  • 3
  • 54
  • 85
  • So you have a tuple type `Where` and want to generically create a tuple of this type by filling in objects from your array (throwing an exception when it doesn't fit)? – Paŭlo Ebermann Dec 07 '15 at 20:01
  • Precisely :) and I trust that the array has elements that successfully can create a Tuple of type Where. – Renato Dec 08 '15 at 18:58

2 Answers2

2

This cannot be done completely type-safely... but assuming that the array indeed contains elements of the correct types as they should appear in a Tuple of type Where, the following function will do the trick:

Tuple<Anything, Anything, Anything> typedTuple({Anything+} array) {
    if (exists second = array.rest.first) {
        return Tuple(array.first, typedTuple({ second }.chain(array.rest.rest)));
    }
    else {
        return Tuple(array.first, []);
    }
}

And apply gets implemented as:

Result apply<Result, Where>(
    [Anything+] array, 
    Callable<Result, Where> fun)
        given Where satisfies Anything[] {
    value tuple = typedTuple(array);
    assert(is Where tuple);
    return fun(*tuple);
}
Renato
  • 12,940
  • 3
  • 54
  • 85
  • Nice ... is this actually guaranteed by the language specification, or just happens to work in the current implementation? – Paŭlo Ebermann Dec 08 '15 at 21:43
  • By the way, an analogous thing works with the sequence constructor: `[*array]` will create an `ceylon.language::ArraySequence`, where `T` is the union of the classes of the elements in the iterable. – Paŭlo Ebermann Dec 08 '15 at 21:56
  • 1
    When you have the meta model of the function (i.e. something implementing `ceylon.language.meta.model.Function`), that already has an apply function which takes an `Anything[]`. (I already used that fact for some codegolf tasks when writing an interpreter of a dynamic language). It doesn't work for a generic Callable, though. – Paŭlo Ebermann Dec 08 '15 at 21:59
  • @PaŭloEbermann each Tuple is constructed with an actual Object for First, and Rest is given another Tuple built in the same way, recursively... All that is required from Tuple for this to work is that it uses the runtime type of the First object, not its static type, which I think can be a safe bet, but the language specification doesn't go into that kind of detail, I believe... so that's unclear. – Renato Dec 09 '15 at 14:29
0

There's nothing relating the type of array to the parameters of fun, so that signature can't possibly be implemented in a type-safe way. You're not constraining the type of array at all; it could contain anything. How in principle would a type-safe implementation handle the case where fun expects [String, Integer] but array is [Boolean+]?

gdejohn
  • 7,451
  • 1
  • 33
  • 49
  • you are correct. But my question assumes the type of array is correct. Because in my code I actually build array using the metamodel by inspecting the Where type. – Renato Dec 08 '15 at 13:49
  • if you are *already* using the metamodel to build the array, then why not just build a correctly typed `Tuple` instead? It may not be the most performant code, but https://gist.github.com/jvasileff/9c5b079f6e1526deed2d does something similar. – John Vasileff Dec 08 '15 at 19:03
  • This is in a completely generic generator code. It checks which types it needs to generate, generates instances by looking up generator functions for the given types, gathers them as [Anything+] and then must convert that to Tuple. – Renato Dec 08 '15 at 19:11
  • "gathers them as [Anything+] and then must convert that to Tuple" were the steps I was suggesting be combined – John Vasileff Dec 08 '15 at 19:12
  • `assert( is Where [Anything+])` fails. using the function I posted, this passes... so I can pass the Tuple to my test-runner. – Renato Dec 08 '15 at 19:13