Fork me on GitHub
#babashka
<
2024-03-16
>
leifericf07:03:30

Is there a way to interop with code written in other languages like C/C++ via Babashka? If not, I have a potentially bonkers out-of-the-box idea: Embed Lua into Babashka (or create a library or pod for Lua?) to leverage Lua's excellent facilities for interop and portability, in addition to all libraries written in Lua (including Fennel). Lua is, after all, designed for such embedded and “glue code” use cases. Embedding Lua into Babashka would dramatically increase Babashka’s surface area and available libraries without increasing its size much (the Lua interpreter built with all standard Lua libraries takes 282K, and the Lua library takes 470K). This would allow us to piggyback on Babashka’s excellent tooling when working with Lua, passing data between Clojure, Lua, and C/C++, and use Babashka for things like game development (my day job). Is that idea in the category of potentially smart or really dumb? :thinking_face: I'm not sure. Thoughts?

clojure 1
👀 1
leifericf10:03:15

Or perhaps it would be better suited as a separate flavor of Babashka, like #nbb or #obb. Hello, #lbb? 😅 But as opposed to Apple Script and Node, Lua was explicitly designed to be embedded into other things. Therefore, I thought it made more sense to embed it in Babashka.

borkdude11:03:07

What you want is already possible with nbb + bun:ffi

👀 1
💡 1
borkdude11:03:54

Another experiment has been done by @U7RJTCH6J once with libffi (or something called like that) in bb

👋 1
borkdude11:03:08

you can probably find some info on this in the github project

👍 1
leifericf12:03:28

Thanks for the pointers! I'll check that out.

respatialized14:03:45

https://gitlab.com/andreyorst/fennel-cljlib you may also be interested in a library that adds more Clojure-like facilities to Fennel, which perhaps is tackling a similar problem from the opposite direction? I feel like trying to mix two lisp dialects by embeddeding Fennel and Lua in Babashka might rapidly become extremely complicated, especially if there are subtle differences in semantics between the different dialects.

👍 1
leifericf15:03:54

Thanks! I'm aware of fennel-cljlib, and it's excellent! It removes many of the Lua foot guns and makes Lua more ergonomic. Unfortunately, Fennel (because of Lua) doesn't offer persistent and immutable data structures and all the fantastic tooling we have with Babashka. I was thinking maybe https://www.lua.org/pil/24.html could be embedded and wrapped in some handy Clojure functions. But then I suppose we might as well build our own interop lib via libffi or something like that, which @U04V15CAJ mentioned.

phronmophobic17:03:45

Yes, I still think c ffi would open up lots of interesting features. Here is the original issue I created, https://github.com/babashka/babashka/issues/978. I've been distracted with other things and haven't been using bb or native image lately, but would be happy to give some pointers if someone was interested. I think integrating libffi is still a good option. Another option is to just wait long enough for project panama to become officially supported.

👀 1
leifericf18:03:32

Thanks. @U7RJTCH6J! I'll check out that issue. Do you have any thoughts on piggybacking on Lua’s C API as it compares to integrating with libffi?

phronmophobic18:03:49

I’m curious if you have any specific c/c++ libraries in mind that you are interested in accessing.

leifericf18:03:35

I'm a game developer working on so-called “AAA games,” specifically MMOs. We use Unreal Engine but heavily customized with a lot of in-house C++. We have embedded Lua to script the game engine primarily for automated testing through some custom C++ mechanisms. I can and will probably look into introducing Fennel as a safer and more user-friendly Lua “frontend.” But I would prefer to use Clojure and thought Babashka might be one possible avenue to explore. https://moonscript.org is another.

👍 1
leifericf18:03:26

By the way, Lua is a de facto industry standard for scripting in game engines. Because it is implemented in pure ANSI C, Lua runs on virtually any hardware, including all gaming consoles. Many programs (such as Adobe Lightroom, Reaper, Neovim, and https://en.wikipedia.org/wiki/List_of_applications_using_Lua) embed Lua to provide users with scripting capabilities.

leifericf18:03:15

Unfortunately, Lua has many gotchas and footguns, such as variables being global by default and mutable everything. Its single data collection type (the “table”) is a weird hybrid of a vector and a map. Those things lead to a lot of confusion and bugs. Fennel, MoonScript, and other “Lua frontends” solve much of that. But none of those are Clojure 😭

phronmophobic19:03:47

I used to work in games and used Lua a bit, but that was a long time ago. Lua has a good c api, so you could also orchestrate Lua from clojure and vice versa if you can make c ffi calls.

👍 1
phronmophobic19:03:30

You can also create standalone shared/static libraries with clojure code using native-image.

👍 1
leifericf19:03:17

Or write a small Clojure interpreter in pure ANSI C with a Lua-like C API 🕳️:man-walking:

leifericf19:03:53

Of course, someone apparently already did that! Haha. https://github.com/rekola/nanoclj

leifericf19:03:14

Oh, I didn't know #C015LCR9MHD could be used as a library from C++. Cool! Thanks again, @U04V15CAJ!

👍 1
eigenhombre23:03:57

I discovered thanks to @nonrecursive’s Babooka that one can do unit tests w/ Babashka and am finding this works very nicely for my purposes. However, my bb.edn task for it is rather awkward:

{:paths ["src" "test"]
 :tasks {:requires (;; ...
                    [babashka.fs :as fs])
;; ...
         test (do
                ;; FIXME: make a better test runner
                (require 'clojure.test)
                (require 'xyz.md-test)
                (require 'xyz.dates-test)
                (let [{:keys [fail error]}
                      (clojure.test/run-tests
                       'xyz.md-test
                       'xyz.dates-test)]
                  (when-not (zero? (+ fail error))
                    (System/exit 1))))}}
i.e. a manual require and argument to run-tests is for each test namespace seems to be required. Does anyone know of a more elegant working idiom / example?

eigenhombre00:03:21

I'll check that out also, thanks!