HMR
HMR

Reputation: 39340

Providing a function that is bound by 3rd party scipt

I am trying to write a riot tag in Fsharp but am unable to do so without changing riot.

In JavaScript I should provide a function like:

function(opts) {
  this.on("update",function(opts){
    this.opts = opts || this.opts;
  });
}

riot will then call this function using Function.prototype.call:

if (impl.fn) { impl.fn.call(this, opts); }

In F# I tried the following:

[<Emit("this")>]
let isThis (x: 'a) : obj = jsNative

let bind fn =
  fun o ->
    fn (isThis 1) o 

[<Emit("$0 === undefined")>]
let isUndefined (x: 'a) : bool = jsNative

bind
  (
    fun me opts ->
      me?on(
        "update"
        ,(
          fun opts ->
            if not (isUndefined opts) then
              me?opts <- opts
              ()
        )
      )
  )

However; the bind function is transpiled to:

export function bind(fn, o) {
  return fn(this)(o);
}

Not currying when I would like it to curry, the output I was looking for is:

export function bind(fn) {
  return function(o){
    return fn(this)(o);
  }
}

The only way I can get this to work is to change riot.js to:

if (impl.fn) { impl.fn(this)(opts); }

And provide my function in F# in the following way:

fun me opts ->
  me?on(
    "update"
    ,(
      fun opts ->
        if not (isUndefined opts) then
          me?opts <- opts
          ()
    )
  )

Changing 3rd party libraries to satisfy transpiler generated code is not ideal. Is there a way for the transpiler to generate the output I'm looking for?

[update]

A better way to do this that doesn't require changing 3rd party code is to provide the bind function as 3rd party JavaScript:

Then import it and use bind in your template code file:

let JSI = 
  bind<(obj -> obj -> unit) -> obj>
    "../../../js/3rd/JSI.js"
bind
  (
    fun me opts ->
      me?on(
        "update"
        ,(
          fun opts ->
            if not (isUndefined opts) then
              me?opts <- opts
              ()
        )
      )
  )

Upvotes: 1

Views: 108

Answers (1)

sdgfsdh
sdgfsdh

Reputation: 37131

You are hitting up against Fable's automatic uncurrying. What you need to do is replace the F# functions with System.Func delegates to prevent Fable from uncurrying.

I was able to get pretty close with this:

[<Emit("this")>]
let jsThis : obj = jsNative

let bind (fn : Func<obj, (obj -> obj)>) = 
  Func<_, _> (fun o -> fn.Invoke(jsThis) o)

The generated JavaScript:

export function bind(fn) {
    return (o) => fn(this)(o);
}

Upvotes: 2

Related Questions