Consider the following F# source code:
module App
let square x = x * x
let result = square 5 // the part in question
Inspecting the ApplyInfo that results from square 5, I get:
{ownerType = DeclaredType (App Module,[]);
ownerFullName = "App";
methodName = "square";
methodKind = Method;
callee = None;
args = [Value (NumberConst (5.0,Int32))];
returnType = Number Int32;
range = Some irrelevant location data
fileName = "path/to/App.fs";
decorators = [];
calleeTypeArgs = [];
methodTypeArgs = [];
methodArgTypes = [Number Int32];
genericAvailability = false;
caughtException = None;}
As you can see, field callee is None and I need to reconstruct the application expression, i.e.:
let appExpr = Fable.Expr.Apply(info.callee.Value, info.args, info.returnType, info.range)
but this doesn't work because when callee = None, info.callee.Value throws an null-reference exception.
I assume by callee you would mean "instance" which not present here because this is a function from the module App. I can see that ownerType field exposes that information about the App module but I am still unable to use the module as the callee inside Fable.Expr.Apply
I was playing around with Babel AST and with Fable.MemberDeclaration I can do whatever I want but the problem with MemberDeclaration is that it does _not_ expose information about the module (ownerType) in which the member is declared (I need that information too).
Any ideas on how to achieve this? :smile: @alfonsogarciacaro @ncave
@Zaid-Ajaj Not sure if that helps at all, but sometimes Fable uses the first argument as an instance if there is no callee. Doesn't look like that's the case here though, so that doesn't help.
@ncave I have seen instanceArgs being used a lot, I just don't know which arguments are being passed to it, in my case, the only arguments I have are the arguments being passing to the function call, nothing more.
In the Replacements module there are cases where one of the args is used as the callee (for example, methods that compile to native JS calls) or where the callee is passed as an arg (like String instance methods that are implemented as module functions in fable-core).
But you're right this should be more evident. In your case, as you want to reconstruct the call in the same way Fable does you need to make a type reference to the owner type (which can be a module) as follows:
let callee =
match i.callee with
| Some callee -> callee
| None -> makeNonGenTypeRef com i.ownerType
Fable.Apply(callee, i.args, Fable.ApplyMeth, i.returnType, i.range)
We use makeNonGenTypeRef to prevent Fable from wrapping the type reference with generic info. Yeah, I know, the API is not very nice, I was building it on the go, but probably needs some work to be usable for consumers :wink:
@alfonsogarciacaro Even if it was built on the go, it is still pretty damn impressive, I am having a hard time navigating through the AST, let alone trying to build ONE expression :joy: I can imagine the gigantic amount of work you (and others, ofcourse) have put in this project to make it all work out, thanks a lot :pray:
Back to the question: unfortunately that alone didn't make it work, it seems that I "lost" the reference to the square function. The original Babel AST generated for square 5 without modifications:
(Value (IdentValue square),[Value (NumberConst (5.0,Int32))],ApplyMeth,
Number Int32,Some {start = {line = 5;
column = 13;};
end = {line = 5;
column = 21;};})
After reading the ApplyInfo and reconstructing the call:
let callee =
match info.callee with
| Some callee -> callee
| None -> makeNonGenTypeRef com info.ownerType
let appExpr = Fable.Apply(callee, info.args, Fable.ApplyMeth, info.returnType, info.range)
The appExpr is compiled to Babel AST into this:
(Value (TypeRef (App Module,[])),[Value (NumberConst (5.0,Int32))],
ApplyMeth,Number Int32,Some {start = {line = 5;
column = 13;};
end = {line = 5;
column = 21;};})
Which looks like as if I am using App module as the function itself
However, since now I had the a reference of App module, I could just get the square function as if I was reading a property:
let callee =
match info.callee with
| Some callee -> callee
| None -> makeNonGenTypeRef com info.ownerType
// get the function from the module:
let func = makeGet info.range info.returnType callee (makeIdentExpr info.methodName)
// apply arguments to the function
let appExpr = Fable.Apply(func, info.args, Fable.ApplyMeth, info.returnType, info.range)
Not sure if this would work for other cases but for now it works like a charm! thanks agian :smile:
The question is answered, forgot to close the issue :smile:
Ah, yes, sorry! I forgot about accessing the member 馃槄 Glad to see you could find it out and thanks a lot for the kind words!
Most helpful comment
@alfonsogarciacaro Even if it was built on the go, it is still pretty damn impressive, I am having a hard time navigating through the AST, let alone trying to build ONE expression :joy: I can imagine the gigantic amount of work you (and others, ofcourse) have put in this project to make it all work out, thanks a lot :pray:
Back to the question: unfortunately that alone didn't make it work, it seems that I "lost" the reference to the
squarefunction. The original Babel AST generated forsquare 5without modifications:After reading the ApplyInfo and reconstructing the call:
The
appExpris compiled to Babel AST into this:Which looks like as if I am using
Appmodule as the function itselfHowever, since now I had the a reference of
Appmodule, I could just get thesquarefunction as if I was reading a property:Not sure if this would work for other cases but for now it works like a charm! thanks agian :smile: