6

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

Stewart_R
  • 13,764
  • 11
  • 60
  • 106
  • 4
    Not sure if this is relevant, but the function isn't really parameterless, it has a single parameter of type unit. – John Palmer Oct 06 '16 at 09:49
  • Yeah, good point but it maps the original 'paramterless' function as expected - it's just when a reference is made to that function that the problem arises – Stewart_R Oct 06 '16 at 10:14
  • What problem exactly? The only issue I can see with `arg00_` is if you pass it to some javascript code that uses `f.length` to get the declared number of arguments, which is highly discouraged practice. Otherwise it shouldn't change anything. – Tarmil Oct 06 '16 at 11:49
  • 1
    @Tarmil When I call `.controller()` from the angularjs library with the generated function it fails with an [unknown provider error](https://docs.angularjs.org/error/$injector/unpr?p0=unitVarProvider%20%3C-%20unitVar%20%3C-%20test) whereas if I intercept the generated javascript code and remove the superfluous argument (with no other changes whatsoever) it works as expected. – Stewart_R Oct 06 '16 at 12:46
  • @Tarmil I'm not sure I managed to explain that properly in my previous comment so I've now edited the question to provide a much fuller explanation of why this is an issue :-) – Stewart_R Oct 06 '16 at 13:08

1 Answers1

8

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 fable-compiler@0.6.12 and fable-core@0.6.8. 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.)