Missing argument and currying confusion

Hey all, I’m just picking up ReScript to write a Chrome extension, and wanted to write some quick bindings to the chrome global namespace available within web extensions. This unfortunately stumped me almost immediately, and from digging around in older forum posts, I think this might have to do with currying?

Here’s the code so far:

type port = {onMessage: unit => unit}

type runtimeOnConnectMethods = {addListener: port => unit}

type runtime = {onConnect: runtimeOnConnectMethods}

type namespace = {runtime: runtime}

@val external chrome: namespace = "chrome"

let onConnectListener = port => {
  port.onMessage(Js.log("ok"))
}

chrome.runtime.onConnect.addListener(onConnectListener)

The error is thrown on the last line:

This has type: port => unit
  Somewhere wanted: port

Can anyone point me in the right direction here? Thanks in advance. I’m coming from a TypeScript background so I’m probably not approaching this the real “Rescript way”.

This isn’t a currying issue, just a mistake in the type of addListener. It says it;s a function that takes a port and returns nothing (a first-order function), but you’re using it as a function that takes another function that takes a port and returns nothing (a second-order function).

The solution is just to fix the type annotations:

type runtimeOnConnectMethods = {addListener: (port => unit) => unit}

Note also that your code is quite unidiomatic, and probably won’t behave as you expect, because addListener is treated as a simple function, not a method call that will bind this, as in JavaScript.

The more idiomatic way is to use modules for namespacing and @send for methods:

module Port = {
  type t

  @send external onMessage: (t, unit => unit) => unit = "onMessage"
}

module Chrome = {
  module Runtime = {
    module OnConnect = {
      @val
      external addListener: (Port.t => unit) => unit =
        "chrome.runtime.onConnect.addLListener"
    }
  }
}

let onConnectListener = port => {
  port->Port.onMessage(() => Js.log("ok"))
}

Chrome.Runtime.OnConnect.addListener(onConnectListener)

I’m not 100% sure that this is correct either though, as it’s not entirely clear from your code whether onConnect is an object with a method or a namespace.

1 Like

Thanks so much @glennsl for pointing out the annotation error, and also explaining a better way to achieve this through modules and @send. I will go this way to be as idiomatic as possible :pray: