[ANN] jlbun: use Julia from Bun

Bun is a new Javascript runtime. It is fast, and it has a very fast bun:ffi.

Based on bun:ffi, I just wrote an experimental package jlbun which enables using Julia from Javascript/Typescript.

The package has been published at NPM, so you can directly install it via npm install jlbun. However, since a thin dynamic library needs to be built locally, cmake is needed. Only Linux and macOS are supported right now, since Bun does not support Windows yet.

To use the package, you need both Julia and Bun. There are a few examples in the repo’s README.

It is still at a very early stage. Feedbacks and suggestions are welcome, especially those related to the following topics:

  • API design
  • Memory management
  • Performance
14 Likes

With the latest update, we can now do something interesting, e.g., combining Julia’s Task with Javascript’s Promise. Here is an example:

import { Julia, JuliaFunction, JuliaTask, JuliaValue } from "../jlbun";

Julia.init();

const promises: Promise<JuliaValue>[] = [];

const func = Julia.eval(`function ()
  println(Threads.threadid());
  ans = 0;
  for i in 1:1000
    ans += i
  end
  ans
end
`) as JuliaFunction;

for (let i = 0; i < Julia.nthreads; i++) {
  promises.push(JuliaTask.from(func).schedule(i).value);
}

const results = (await Promise.all(promises)).map(promise => promise.value);
console.log(results);

Julia.close();
3 Likes

Very interesting. Since Julia can ccall libjulia, we can do the following trick:

const interpolated = `
  function ()
    ptr = ccall(:jl_call, 
      Ptr{Nothing}, 
      (Ptr{Nothing}, Ptr{Nothing}, Cint),
      convert(Ptr{Nothing}, ${funcPtr}),
      convert(Ptr{Nothing}, ${argsPtr}),
      ${argsLength},
    )
    
    unsafe_pointer_to_objref(ptr)
  end
`;

Just eval() the interpolated string above, and we will get a zero-argument version of the original function. The wrapped function can then be used to initiate a Task.

1 Like

Using Bun’s JSCallback, we can now use JS functions in the Julia scope:

import { Julia, JuliaArray, JuliaFunction, safeCString } from "jlbun";

Julia.init();

const jsFunc = (x: number) => {
  return safeCString(x.toString());
};

const cb = JuliaFunction.from(jsFunc, {
  returns: "cstring",
  args: ["i32"],
});

const arr = JuliaArray.from(new Int32Array([1, 10, 20, 30, 100]));
Julia.Base["sort!"].callWithKwargs({ by: cb, rev: true }, arr);
Julia.println(arr); // Int32[30, 20, 100, 10, 1]
cb.close();

Julia.close();
2 Likes