Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fable F# to Javascript: Parameterless functions being given a parameter when referenced

Tags:

f#

fable-f#

I am having difficulty referring to parameterless functions in Fable.

With this example:

let f1 () = 
    1

let someRefTof1 = f1

I'd expect the generated js to look something like this:

function f1() {
    return 1;
}

var someRefTof1 = f1;

but what I actually get is:

function f1() {
    return 1;
}

var someRefTof1 = exports.someRefTof1 = function someRefTof1(arg00_) {
    return f1(arg00_);
};

I'm unclear on the purpose of arg00_ or how to avoid it being generated?

(As a bit of background, I am struggling to call a function in an external js library which expects a function to be passed as a parameter)


Edit:

Background

The above is what i believe to be a minimal, verifiable, reproduction of my question but, after comments, I thought it may be useful to provide a touch more context on why this is causing issues. What I am actually trying to do is use angularjs from Fable.

So my example looks more like this:

let app = AngularFable.NgFable.angular.``module``("app",[||])

type TestCtrl() = 
    member this.Val1() = "boom";

app?controller("test", TestCtrl)

which gets compiled to:

var app = exports.app = angular.module("app", []);

var TestCtrl = exports.TestCtrl = function () {
    function TestCtrl() {
        _classCallCheck(this, TestCtrl);
    }

    TestCtrl.prototype.Val1 = function Val1() {
        return "boom";
    };

    return TestCtrl;
}();

_fableCore.Util.setInterfaces(TestCtrl.prototype, [], "App.TestCtrl");

app.controller("test", function (unitVar) {
    return new TestCtrl();
});

with unitVar being the problematic parameter introduced in this example. When I use this in my html with something like:

  <div ng-app="app">
    <div ng-controller="test as vm">
      {{vm.Val1()}}
    </div>
  </div>

I run into an unknown provider error whereas if I simply change the compiled javascript to remove the unitVar parameter from the last line like this:

app.controller("test", function () {
    return new TestCtrl();
});

then my example works as expected.

I'd really like to know if there is a way to avoid having the Fable compiler generate this parameter. I'm 99% sure this reduces to the same problem as in my original question but I've included this additional context to better explain why this is an issue

like image 239
Stewart_R Avatar asked Oct 06 '16 09:10

Stewart_R


1 Answers

Thank you very much for your question and detailed explanations. There're two things here that are a bit tricky and are caused by optimisations both of the F# compiler and Fable.

  • In the AST provided by the F# compiler, methods (functions that are members of a type or module) are compiled as usual methods as in C#. This is for optimization.
  • However, when you create an anonymous lambda or make a reference to a method, the F# compiler will keep F# semantics, that is, all functions have a single argument (as John Palmer says, unit is an argument too) and can be curried.

Ok, this info is just to make clear why the F# compiler/Fable represent methods and lambdas differently. Let's go with the issue of argumentless functions: the obvious solution would be of course to remove the F# compiler generated argument for functions accepting unit (as it's already done for methods). In fact, I also had problems with libraries like Mocha because of this.

I did try to remove the unit argument at the beginning but I got fails in some scenarios because of this. TBH, I don't remember now exactly which tests were failing but because of the expectation that there'll be always an argument, in some cases function composition or inlining was failing when the unit argument was removed.

Other attempts to modify the semantics of F# functions in the JS runtime have always failed because they don't cover all scenarios. However, we can be more lenient with delegates (System.Func<>) as it's usually safe to assume these ones should behave more like functions in languages like C# or F#. I can try to remove the unit argument just for delegates and see what happens :)

For more info about sending F# functions to JS code you can check the documentation.

UPDATE: Scratch all that, please try [email protected] and [email protected]. This version eliminates unit arguments, the solution was actually simpler than I thought and (hopefully) shouldn't create issues with existing projects. (The explanation about methods and lambdas compiled differently still applies.)

like image 82
Alfonso Garcia-Caro Avatar answered Sep 19 '22 11:09

Alfonso Garcia-Caro