How to keep an unused variable for its side effects (binding to JS library)?

I’m writing bindings for Babylon, a JS game engine library, which commonly uses side effects in constructors. For example, when you instantiate a new ArcRotateCamera, it will add a camera to your scene, allowing the user to look around inside their browser window at the 3D world.

I’ve modeled these constructors using @new. For example, for ArcRotateCamera:

type t

@new @module("@babylonjs/core/Cameras/arcRotateCamera")
external make: (
  ~name: string,
  ~alpha: float,
  ~beta: float,
  ~radius: float,
  ~target: Babylon__Vector3.t,
  ~scene: Babylon__Scene.t,
  ~setActiveOnSceneIfNoneActive: bool=?,
  unit,
) => t = "ArcRotateCamera"

When I use this binding, it looks kind of like this:

let camera = ArcRotateCamera.make(
  ~name="camera",
  ~alpha=1.0,
  ~beta=1.0,
  ~radius=30.0,
  ~scene,
  ~target=Vector3.make(~x=0.0, ()),
  (),
)

The problem is that I don’t use camera, so ReScript compiles the instantiation out since it results in an unused variable. That means that the all-important side effect of adding the camera to the scene never fires.

Is there a way to force ReScript not to discard the camera variable? Or might there be a more idiomatic way to solve my problem? For some reason, I can’t just do ArcRotateCamera.make(...) without assigning it, it doesn’t seem to like the return value and it gives me a Somewhere wanted: unit error.

I’m not sure but you could try

  1. Assign to an underscore (_)
let _ = ArcRotateCamera.make(
  ~name="camera",
  ~alpha=1.0,
  ~beta=1.0,
  ~radius=30.0,
  ~scene,
  ~target=Vector3.make(~x=0.0, ()),
  (),
)
  1. Or, pipe to ignore function
ArcRotateCamera.make(
  ~name="camera",
  ~alpha=1.0,
  ~beta=1.0,
  ~radius=30.0,
  ~scene,
  ~target=Vector3.make(~x=0.0, ()),
  (),
)->ignore

Hi, I tried your code in the Playground. ReScript did not compile the instantiation out.

Curious why it makes a difference?

This just gets rid of the compiler warning about an unused variable, but since the variable is still unused, the compiler still removes it.

This is exactly what I was looking for, thank you! I had forgotten about ignore.

You’re right, it doesn’t compile it out. Weird. Not sure what’s causing the discrepancy.

In Babylon, instantiating objects both creates them and adds them to the scene. It’s a common pattern in that library (see above). So, the instantiation line must not be compiled out or else this important side-effect won’t happen.

Another option is to prefix the variable name with an underscore:

let _camera = ArcRotateCamera.make(...

This should also stop ReScript from compiling it out.

1 Like