Stream: beginners

Topic: ruby-interop fails


view this post on Zulip dank (Feb 13 2023 at 22:45):

[nix-shell:~/dev/roc/examples]$ cd ruby-interop/ && ../../target/release/roc build --lib
0 errors and 0 warnings found in 194 ms while successfully building:

    libhello

[nix-shell:~/dev/roc/examples/ruby-interop]$ ruby extconf.rb
checking for -lhello... no
creating extconf.h
creating Makefile

[nix-shell:~/dev/roc/examples/ruby-interop]$ make
compiling demo.c
linking shared-object demo.so

[nix-shell:~/dev/roc/examples/ruby-interop]$ irb
irb(main):001:0> require_relative 'demo'
Traceback (most recent call last):
        5: from /nix/store/2xvc6i6dnar93p1wvh335jjsgmmjqg5c-ruby-2.7.7/bin/irb:23:in `<main>'
        4: from /nix/store/2xvc6i6dnar93p1wvh335jjsgmmjqg5c-ruby-2.7.7/bin/irb:23:in `load'
        3: from /nix/store/2xvc6i6dnar93p1wvh335jjsgmmjqg5c-ruby-2.7.7/lib/ruby/gems/2.7.0/gems/irb-1.2.6/exe/irb:11:in `<top (required)>'
        2: from (irb):1
        1: from (irb):1:in `require_relative'
LoadError (/home/dankey/dev/roc/examples/ruby-interop/demo.so: undefined symbol: roc__mainForHost_1_exposed_generic - /home/dankey/dev/roc/examples/ruby-interop/demo.so)

I think i did everything right?

ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
clang version 13.0.1
GNU Make 4.3

i actually am not sure how that's supposed to work
both host.c and demo.c are declaring an external roc__mainForHost_1_exposed_generic, but who implements it? :thinking:

view this post on Zulip Brian Carroll (Feb 13 2023 at 22:54):

The Roc app implements it. In the Roc code it would just be called mainForHost

view this post on Zulip Richard Feldman (Feb 14 2023 at 00:22):

hm, so it looks like extconf.rb isn't finding libhello

view this post on Zulip Richard Feldman (Feb 14 2023 at 00:22):

when I run it, I get this:

$ ruby extconf.rb
checking for -lhello... yes
creating extconf.h
creating Makefile

view this post on Zulip Richard Feldman (Feb 14 2023 at 00:22):

so yes instead of no after checking for -lhello

view this post on Zulip Richard Feldman (Feb 14 2023 at 00:22):

admittedly I haven't tried it on NixOS

view this post on Zulip Richard Feldman (Feb 14 2023 at 00:23):

so maybe extconf.rb is passing -lhello to clang or ld or something, and on NixOS it doesn't look in the current directory for a libhello?

view this post on Zulip Richard Feldman (Feb 14 2023 at 00:23):

or maybe it needs a different file extension on libhello?

view this post on Zulip dank (Feb 14 2023 at 08:34):

Richard Feldman said:

or maybe it needs a different file extension on libhello?

yea that's what i suspected. there's libhello.so.1.0. not sure how to validate that that's what wrong

also it might be a nixos thing, yes

view this post on Zulip dank (Feb 14 2023 at 08:36):

BTW full discretion I actually barely know ruby at all, but I figured if i can get it working and understand how it works, I might be able to replicate your work for a python-interop, which shouldn't be that hard

view this post on Zulip dank (Feb 14 2023 at 08:38):

in the meantime I'll maybe test on my debian machine to see if it actually is just a nixos thing

view this post on Zulip dank (Feb 14 2023 at 08:58):

also changing the extension to libhello.so etc doesn't make a difference ruby still doesn't get it
i figured maybe playing with dir_config would solve it but doesnt seem so
(even creating a lib dir and moving libhello.so there, weird stuff)

view this post on Zulip dank (Feb 14 2023 at 09:49):

doesn't work on debian either
(as in nixos it says no after checking for -lhello)

view this post on Zulip dank (Feb 14 2023 at 09:49):

I assume @Richard Feldman you're using macos?

view this post on Zulip Richard Feldman (Feb 14 2023 at 13:35):

yup, interesting

view this post on Zulip Richard Feldman (Feb 14 2023 at 13:36):

one of my coworkers get it working at NoRedInk on Linux, but I forget what the trick was

view this post on Zulip Richard Feldman (Feb 14 2023 at 13:36):

I think it had to do with file extension though - did you try libhello.so.1?

view this post on Zulip Richard Feldman (Feb 14 2023 at 13:36):

cc @Juliano

view this post on Zulip dank (Feb 14 2023 at 14:09):

Richard Feldman said:

I think it had to do with file extension though - did you try libhello.so.1?

yes. no change

view this post on Zulip Juliano (Feb 14 2023 at 14:40):

i don't remember hitting this mainForHost problem specifically

view this post on Zulip Juliano (Feb 14 2023 at 14:40):

lemme look at our setup to see what extra we got compared to dank's stuff

view this post on Zulip Juliano (Feb 14 2023 at 14:42):

we had to ln -sf libhello.so.1.0 libhello.so.1 and ln -sf libhello.so.1 libhello.so, else demo.so wouldn't be properly linked (different error tho)

view this post on Zulip Juliano (Feb 14 2023 at 14:43):

On Linux, for this to work we also needed to set either LD_LIBRARY_PATH to this directory or LD_PRELOAD to the absolute path of libhello.so

view this post on Zulip Juliano (Feb 14 2023 at 14:44):

i think patchelf could've helped avoid LD_LIBRARY_PATH stuff, but i was lazy

view this post on Zulip Juliano (Feb 14 2023 at 14:47):

it might be some difference in demo.c? also we're using a pretty old version of roc (3-4mo old?)

view this post on Zulip Juliano (Feb 14 2023 at 14:47):

this is my demo.c https://gist.github.com/omnibs/e6c09b8fee711549d7009e234c34f438

view this post on Zulip Juliano (Feb 14 2023 at 14:50):

might also be something with your roc lib's platform? our PoC used this: https://gist.github.com/omnibs/8cd4eedae5f4c645fc857c86bc213d6a

view this post on Zulip Juliano (Feb 14 2023 at 14:51):

then you use it in your lib's main.roc like so:

app "libhello"
    packages { pf: "platform/main.roc" }
    imports []
    provides [makeItRoc] to pf

makeItRoc : Str -> Str
makeItRoc = \str ->
    if Str.isEmpty str then
        "I need a string here!"
    else
        "\(str), OH YEAH!!! 🤘🤘"

view this post on Zulip dank (Feb 14 2023 at 19:08):

the other stuff mentioned didnt seem to make a difference but

[nix-shell:~/dev/roc/examples/ruby-interop]$ LD_PRELOAD="$(pwd)/libhello.so.1.0" ruby extconf.rb
ruby: symbol lookup error: /home/dankey/dev/roc/examples/ruby-interop/libhello.so.1.0: undefined symbol: roc_alloc

view this post on Zulip dank (Feb 15 2023 at 23:29):

good news bad news
bad news is that the ruby-interop doesn't work and idk how to make it work

good news:

view this post on Zulip dank (Feb 15 2023 at 23:30):

image.png

view this post on Zulip dank (Feb 15 2023 at 23:30):

gottem

view this post on Zulip Brendan Hansknecht (Feb 15 2023 at 23:38):

haha. cool

view this post on Zulip Richard Feldman (Feb 16 2023 at 00:08):

yooooo awesome!!!

view this post on Zulip Richard Feldman (Feb 16 2023 at 00:08):

want to make a PR to add that to examples?

view this post on Zulip Richard Feldman (Feb 16 2023 at 00:09):

that would be our second dynamic language interop example ever!

view this post on Zulip Luke Boswell (Feb 16 2023 at 05:43):

Wow, this is very cool!!

view this post on Zulip dank (Feb 16 2023 at 15:51):

Richard Feldman said:

want to make a PR to add that to examples?

PR's up :)

view this post on Zulip dank (Feb 16 2023 at 23:21):

image.png
why does it say it like that
sounds so rude lol

view this post on Zulip dank (Feb 16 2023 at 23:22):

in any case @Richard Feldman I added zulip link + some readme cleanup bc it seemed not great + some key notes on the demo.c implementation
hope that's better

view this post on Zulip Richard Feldman (Feb 17 2023 at 00:27):

looks great, thank you - approved!

view this post on Zulip Folkert de Vries (Feb 17 2023 at 18:23):

the ruby interop example is/was broken. I'm not sure it ever really worked

view this post on Zulip Richard Feldman (Feb 17 2023 at 19:04):

it definitely did on a M1 Mac (I haven't tried recently though; it may have regressed), and Juliano and I used it to get Ruby interop working on NixOS

view this post on Zulip dank (Feb 17 2023 at 23:03):

good thing I stumbled upon that ruby-interop example though

image.png
I'm no expert, but I'd say that's pretty dank

view this post on Zulip Brendan Hansknecht (Feb 18 2023 at 01:25):

Lots of languages can speak c. Though some may make it hard to deal with more complex roc types

view this post on Zulip Richard Feldman (Feb 18 2023 at 03:02):

yoooo that's awesome!!!

view this post on Zulip Richard Feldman (Feb 18 2023 at 03:02):

is that using JNI?

view this post on Zulip dank (Feb 18 2023 at 09:04):

yes indeed
gotta say JNI is much nicer to work with than I anticipated

view this post on Zulip dank (Feb 18 2023 at 11:34):

actually panama might be better for us
cause jextract can auto generate the java glue

meaning the user wouldn't need to write any java (including the static load and native method decls)

so that he could, from any jvm lang at all (be it clojure scala or whatnot), build the deps and import functions from the java glue

view this post on Zulip dank (Feb 18 2023 at 11:47):

also that would make it work for jshell
bc jshell and JNI do not play nice as it turns out

view this post on Zulip Richard Feldman (Feb 18 2023 at 17:35):

interesting! Would any of the JNI alternatives require a third-party dependency?

view this post on Zulip Richard Feldman (Feb 18 2023 at 17:36):

when it comes to interop, I generally try to avoid third-party deps if possible

view this post on Zulip dank (Feb 18 2023 at 18:08):

damn i thought it wouldn't but it seems that openjdk, even 19 only contains only the api for it,
and the jextract binary ships separately
so it is kinda third party dep https://jdk.java.net/panama/

view this post on Zulip dank (Feb 18 2023 at 18:09):

btw are you ok with having https://github.com/sheredom/json.h ?
java doesn't have a json parser

view this post on Zulip Richard Feldman (Feb 18 2023 at 18:09):

yeah seems reasonable as a starting point

view this post on Zulip Richard Feldman (Feb 18 2023 at 18:10):

eventually JSON shouldn't be necessary, it's just a really convenient way to get interop with a given language up and running :big_smile:

view this post on Zulip Richard Feldman (Feb 18 2023 at 18:10):

you mean for an examples/ entry, yeah?

view this post on Zulip dank (Feb 18 2023 at 18:10):

yea

view this post on Zulip Richard Feldman (Feb 18 2023 at 18:10):

yeah sounds good!

view this post on Zulip dank (Mar 07 2023 at 23:56):

just letting u know i haven't forgot about the jvm-interop, just ran into some issues trying to generalize to langs like clojure
right now kinda waiting for the headless release to come out so my mate who's a jvm god will help smooth out some rough jvm edges

view this post on Zulip Richard Feldman (Mar 08 2023 at 01:47):

nice, thanks for the update - looking forward to it! :smiley:

view this post on Zulip Anton (Mar 08 2023 at 09:11):

What OS is your friend using @dank? I'll make sure to test the headless release on that OS once it's ready

view this post on Zulip dank (Mar 08 2023 at 09:13):

debian

view this post on Zulip dank (Mar 11 2023 at 19:59):

hello hello
i think jvm-interop is ready
https://github.com/dankeyy/roc/tree/jvm-interop/examples/jvm-interop

tested on java kotlin and scala

couldn't get clojure to work because its import system is weird.
people suggested all kinda import tools and deps which i didn't want to dive into.
if anyone here though got a simple solution that'd be great

also i kinda ditched the json encoding part, hope that's ok (?)
Was kinda trivial to just grab the bytes, but tell me if that's bad somehow and we can switch back

also have to say jvm Strings are an absolute meme https://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp16542
spent way too long trying to find a way around it that wouldn't require rewriting utf8 lmao, but i did find a pretty nice simple solution which is to convert it first into a java byte array, then to string. by that "outsourcing" the conversion to the jvm

so yea pretty fun. tell me what you think, and ill pr if it's ready

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:02):

dank said:

also i kinda ditched the json encoding part, hope that's ok (?)

yeah totally! That was just a way to quickly get a more useful proof-of-concept than just sending (for example) strings or bytes across, without having to go as far as to write translations for all the target language's datatypes :sweat_smile:

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:03):

of course if you want a fully-featured interop, you'd need to actually do that (e.g. in the Ruby one, converting not just Ruby strings to/from Roc strings, but also Roc Lists to Ruby Arrays...etc)

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:03):

but you gotta start somewhere, and these examples that just demonstrate the interop with one datatype are still super useful!

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:06):

that totally looks ready to PR to me

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:07):

wow, I'm impressed by how little code it takes on the JVM side

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:07):

I was expecting a lot more!

view this post on Zulip dank (Mar 11 2023 at 20:13):

yeaa, was really surprised by jni here
also in the beginning the bridge function was more involved but i managed to cut some stuff out which was great

view this post on Zulip dank (Mar 11 2023 at 20:15):

Richard Feldman said:

but you gotta start somewhere, and these examples that just demonstrate the interop with one datatype are still super useful!

yea it'd be nice if we'd have that but i feel like a break from jvm lol

so i say we pr it and if anyone here want to interop arrays too, build on it !

view this post on Zulip Richard Feldman (Mar 11 2023 at 20:17):

yeah totally!

view this post on Zulip dank (Mar 11 2023 at 21:30):

happy to say clojure example works aswell
turns out in clojure u need to mention the path like so clj -Sdeps '{:paths ["."]}'

view this post on Zulip Richard Feldman (Mar 11 2023 at 21:34):

ooo nice!

view this post on Zulip dank (Mar 14 2023 at 19:47):

so i wanna make the interops more complete and i have a couple questions

  1. what do you think are compelling function examples for interops?
    needs to be cool, but not too complex as to not distract from the subject
    something like take this java array + scalar and return a new java array with all elements multipled by the scalar?

  2. to pass the platform a List <N>, I need a RocBytes+init_rocbytes of appropriate size, right?
    but what if i have a couple of those
    like i want List U8 and also List U32
    RocBytes can only have one definition :thinking:

view this post on Zulip dank (Mar 14 2023 at 19:59):

oh wait i might be dense
The "RocBytes" name isn't demanded by the platform

yea nvm about the second point then
I can just make RocBytesN structs and populate them however i want from roc side, i think

view this post on Zulip dank (Mar 14 2023 at 21:57):

i also understand host.c serves no purpose in my impl. kinda carried over from ruby interop (looking back im not even sure why it has a main)

like all of the necessary logic is in bridge.c
@Richard Feldman you know what im saying?

view this post on Zulip dank (Mar 14 2023 at 22:02):

and if no decoding is necessary, then the indirection between platform/main.roc and main.roc is redundant (all the platform/main.roc) does it call the app's function

view this post on Zulip dank (Mar 14 2023 at 22:04):

rather it's not that the platform is redundant
it's that the app isn't really an app, it just serves like a proxy here

it might just be that a language interop is a special case but it seems to me that here, all logic is platform logic

view this post on Zulip dank (Mar 14 2023 at 22:05):

to make a long story short i feel like all that is needed here is one .c file and one .roc file


Last updated: Jul 05 2025 at 12:14 UTC