Open lucaswerkmeister opened 7 years ago
I’ve realized that the second suggestion can also be interpreted as part of the more general “undressing” operation that should take place whenever a value “passes into the dynamic domain”, so to speak (#6001 and #6308). Just like an Array
must be unwrapped, a Callable
must be wrapped with something that dresses the arguments.
The first option sounds too invasive. We'd have to emit statements to dress up args in every function. The second option is way better; I don't know if we'd need to emit a custom function at runtime or maybe just call a native one and pass it parameter info (this last option sounds better).
Well, upon further reflection, I think the first option would only have to do this for arguments of dynamic type? Not sure… but I agree, the second one is better.
Having a native function would probably be better, that’s a great idea. I guess it would accept the function itself as well as its callable type (or perhaps just the tuple of its argument types). (I briefly thought it might accept a meta reference, which would be function + meta info, but this needs to work for local and anonymous callables too, which don’t have a metamodel.)
I’ve looked a bit more into this. I think the helper function will accept two arguments: the callable itself, and the Arguments
type argument of the Callable
(printed with typeNameOrList
), e. g. {t:m$1.Empty}
or m$1.mtt$([…])
. It will then walk the arguments
array and dre$$
each argument with the corresponding type from that Arguments
type argument. (If there are more actual arguments than the function expects, ignore them – the function won’t access them.)
I’m not sure how to do that last part. We need to get the ith type from something that might be a Tuple
, Sequence
, Sequential
, Empty
, or any union or intersection of them. And it seems tuple types aren’t actually linked lists in JS, so stripping off the first type (i. e., consuming one argument) at least isn’t completely trivial.
@chochos does something like this exist already? natc$
dresses an array, but it only uses a single type.
The structure of the function makes sense. As for the arguments:
dre$$
each arg)undefined
, and/or Empty
if variadic last arg.Singleton
, perhaps? Are there any guarantees on what kind of Sequence
a sequential argument is?)natc$(…).sequence()
for that?undefined
for a variadic last arg as well, the function header substitutes m$1.empty()
in that case. So that should make this simpler – I don’t explicitly need to pass undefined
, I can just keep the arguments array shorter.Am I seeing this correctly – a variadic tail of a tuple element is just the sequential type as normal part of the tuple type’s type list, with the seq
member set to 1 for a *
tail and to 2 for a +
tail?
So I guess the algorithm could be:
Sequential
/Sequence
element type, set seq
, wrap in {t:'T',l:[…]}
).!seq>0
, pop off one argument, dre$$
it, and push it onto the new arguments array (native JS array).seq==2
, assert there is at least one more argument.dre$$
it, and add it to a tail array.tpl$
.apply
the original function with the new arguments array.Wait, I didn’t take defaulted parameters into account at all here.
Erm. Neither… it seems… does the JS backend…?
Okay, sure, this is a lot easier if I don’t need to worry about union types here :D but how do I decide if a missing argument is an error or acceptable? Just assume every parameter is defaulted?
Am I seeing this correctly…
No, I think I was looking at the wrong kind of type. seq
1 and 2 still seem to be present, but the type is a union of Sequential<T>
/Sequence<T>
and just T
:
Arguments$Callable:m$1.mtt$([{t:'u',l:[{t:m$1.Sequential,a:{Element$Sequential:{t:m$1.$_String}}},{t:m$1.$_String}],seq:1}])
@chochos do you know what that union is doing there? I feel like it might cause some bug (I found #6891 while investigating it, but that seems to be unrelated).
Okay, it looks like I need to use the parameter info (jsc$2
’s parms
parameter), not the Arguments
type argument of Callable
(in jsc$2
’s targs
parameter).
print(identity<String>);
print((String s) => s);
compiles to
m$1.print(m$1.jsc$3(0,m$1.identity,{Value$identity:{t:m$1.$_String}}));
m$1.print(m$1.jsc$2((function($3){return $3;
}),[{nm:'s',mt:'prm',$t:{t:m$1.$_String}}],{Arguments$Callable:m$1.mtt$([{t:m$1.$_String}]),Return$Callable:{t:m$1.$_String}}));
In the first case, there’s no targs
referring to Callable
(the value present is the type arguments of print
!), but I think can still get the parms
info via f.$crtmm$().ps
.
Alright, I’ve pushed my current stab at this to master-for-typescript
. Works for a tiny test module, but runs into problems in the TypeScript converter because of some weirdness related to ceylon-crypto which I haven’t yet been able to fix. (It seems that an argument that’s supposed to only get Asn1Value<Anything>
s also gets Option
values.)
Motivating example, from the TypeScript loader:
program
and thussourceFile
are dynamic, and so isforEachChild
.visitDeclaration
is a Ceylon function with two parameter lists.The internals of
forEachChild
are opaque to Ceylon. All we know is that it receives aCallable
. If it calls thatCallable
, it may do so with undressed values, ones which Ceylon has not yet seen at all. And yet, the body ofvisitDeclaration
, an ordinary Ceylon function, expects them to be dressed, since its parameters are not dynamic. At which point do the arguments get dressed?visitDeclaration
’s body that dress every parameter with its static type. SincevisitDeclaration
might be in a different module than the code that passes it into a dynamic function, we would have to do this for every Ceylon function. Probably expensive.forEachChild
, which first dresses all parameters with their static type before delegating to the realvisitDeclaration
.@chochos wdyt?