How to write an OCaml program that generates a java program based on a java API?

An OCaml program is supposed to talk with some java-based software.
This software provides a java API that allows to create a plugin to it.
I don’t want to write in java this plugin for all reasons you can imagine.

I’ve found theses resources:

opam - javalib : “Javalib is a library that parses Java .class files into OCaml data structures. Javalib offers primitives to extract information from, manipulate, and generate valid .class files.”

opam - sawja : “… Whereas Javalib is dedicated to isolated classes, Sawja handles bytecode programs with their class hierarchy and control flow algorithms.”

GitHub - xavierleroy/camljava: Low-level OCaml/Java interface seems created in 2002 and has a v0.5 in 2024. And is not on opam.

http://ocamljava.org/ is still “under construction” for ages (at least 10 years). And is not on opam.

And there are other opam packages related to “java” :

Name Latest version Description
atdj 3.0.1 Java code generation for ATD
diffast-langs-java 0.3.6 Java parser plugin for Diff/AST
diffast-langs-java-parsing 0.3.6 Java parser for Diff/AST
diffast-langs-java-parsing-cli 0.3.6 Java parser CLI
diffast-langs-java-spec 0.3.6 Java parser spec for Diff/AST

What is the more easy, fast and reliable way to program in Ocaml a java plugin based on a java API?
Tools are:

  • a set of relevant OCaml packages
  • a java API to the target software
  • an OCaml source program

The deliverable is:

  • a java plugin (that conforms to the provided java API)

I think trying to embedded a foreign language in the JVM is probably too complex for you seriously look at it as a solution here. My options would be:

  • bite the bullet, modern Java isn’t that bad, if the plugin is small this is the less painful option
  • run your OCaml plugin in a different process, you still need to write a bit of Java to manage the IPC between your OCaml process and the JVM, but it should be small.
  • JNA, expose your OCaml code as a native library and call it from a thin Java layer.
1 Like

Another option also not using OCaml but close enough: if the plugin can be written in Java, it can probably be written in Scala, which compiles to JVM bytecode. And Scala 3 is a very ML-like language with exhaustive pattern matching and a lot of other powerful features.

2 Likes

Maybe I was not that clear.

I don’t want to embed OCaml-based weird bytecode in a JVM.

And forget about the OCaml program driving directly some software (this sentence was confusing - and this is an option) .

I need to create a java plugin based on some java API, and I can sure do it in Java. At slow pace. So, I would like to:

  • Read the java API and get it in OCaml world
  • Create the program that I need in OCaml that uses this library
  • Generate a Java program from that OCaml program

Hence, there would be nothing else than genuine Java bc in the JVM.

ocamljava seems to be able to generate a java program (hence java bc) from an OCaml program. However just for OCaml 4.01.0 , which is not a problem.

So, I think I « just » need a reverse tool to read the java API and recreate it in OCaml world as an ordinary library.

Then I can use it to write my OCaml program. And use ocamljava to generate my genuine java plugin.

javalib (classes) and especially sawja (classes and all classes hierarchy and algorithms) may be an option.

But the experience of someone who already did that would confirm feasibility and the tool to use.

Thanks for this alternative.

However, it seems more straightforward to relearn java and stay in java world.

Basically, you need to implement some Java interfaces? I have only some vague recollections of ocamljava but why can you not do it directly in it?

Also have you seen this - OCaml-Java: accessing OCaml code from the Java language ?

It may allow you to write in Java just a minimal wrapper which conforms to the required interface and delegate to OCaml which does the actual work.

Yes, I think I need to “implement some java interfaces”.
I can write it in java (but I want to avoid that…).
I prefer/want to write it in OCaml.

Steps are:
step 1 : turn the java API into an OCaml API → How to do that?
step 2 : write an Ocaml program → OK
step 3 : generate a java program from the OCaml program → ocamljava seems fine for doing that ; with some possible limitations that I need to fully identify ; at least: “OCaml-Java 2.0 alpha is based on OCaml version 4.01.0, and requires a Java 1.7 virtual machine to run compiled programs” while I have JVM version 8 update 441 installed → will it work with last JVM?

Also, I need to know if I can freely write a functional program or if I must write an object OCaml program (probably not because a module with type and values should map to a class with attributes and methods : I think there is an isomorphism).
Or if it’s simply recommended to write an object OCaml program with classes and objects.

Java 9 removed the JAXB library from JDK, which should be added separately, but other than you should be safe. I’ve run program compiled for Java 8 with Java 25 with a minimal change to its invokation java --add-opens=...

As for your plan, I do not think it will work. I see it more like that

Step 1. Write a minimal Java adapter

class A implements plugin.IntfA {
 plugin.intfA delegate;

public void interbulate(String s) {
  delegate.intertubulate(s)
}

Step 2. Write the delegate in ocamljava

Java.proxy "plugin.IntfA" (object
  method intertubulate s = ..
)

Wire everything together.

But if you need to have a lot of conversion from Java objects to OCaml value it could be simpler to just write in Java.

1 Like

Sure, I could write the plugin in Java.
Java appears to be much less verbose than 10-20 years ago. And it has even lambda since 2014/Java 8… See https://medium.com/@aravindcsebe/lambda-expressions-in-java-8-a-complete-guide-part-1-ddb516ed85bd Why using OCaml, then?…

But I wonder whether I can get a nice path from my favourite PL where I’m productive to this (“damned”) java” (nice) API.

Which OCaml gen tools could automatically handle the adapter/delegate you suggest? (it’s just boring and time consuming boilerplate).

I’m not aware of any. The ocamlwrap tool from ocamljava does something similar in an opposite direction. Write a simple demo.ml and do a

ocamljava -java-package demo -c demo.ml
ocamlwrap demo.cmi

And judge for for yourself.

UPDATE: For something as simple as the proposed scheme I will probably write a generator using Java reflection. I know that is not what you are asking for, but is probably the simplest way.

  • Class<?> clazz = Class.forName(“plugin.IntfA”);
  • Method[] = clazz.getMethods()
  • method.GetName()
  • method.getParameterTypes()
  • method.getReturnValue()

and with all that data it should be relatively trivial to generate the wrappers.

I’m not a big fan of the AI craze, but aren’t step 1 and 3 something LLM should be good at?

Yes, you are right, « java of ocaml » seems fine enough for getting a Java program from an Ocaml program.

I was asking experienced people for the reverse way in a easy, simple and reliable manner.

I’ve studied in details the targeted java API. It’s quite huge because it covers “everything”, but it appears that I can use a limited number of constructors (classes, attributes/methods).

Hence, there are 2 ways :

  • writing directly in Java, using textual templates, but it’s in java world…
  • writing a program in OCaml that handle the minimal set of required concepts and generate java expressions

Writing an Ocaml program should be:

  • define the minimal set of concepts with types/values : EASY
  • write the functions I need : EASY
  • write a generator that will generate strings in Java syntax from these functions : THIS IS THE POINT

OCaml is good at doing that (handling symbolic expressions, pattern matching…).
I can write a generator from scratch, but I need to redefine Java basic syntax. Or I can reuse it from existing libraries (ocamljava, javalib, sawja…).
Do you agree with this approach?

I’m currently reading the libraries I listed herebefore to find java syntax I can leverage.

PS: I won’t write an Ocaml program that drives directly the java software because a plugin is enough to extend it. Later, I think I can add in the java plugin a communication module for interoperating with an OCaml program (request/response).

UPDATE: because I just need a limited set of API classes, for sequences (CRUDing), a straightforward way, without any dependency (except stdlib), is:

  • write template of targeted java functions with placeholders:
    main_first_sentence
    f1, f2, f3...
    main_last_sentence

  • write OCaml functions that just define name and parameters for each java instance
    compose... main_first_sentence f1 f2 f3... main_last_sentence
    Printf.sprintf to get a string

  • output (outchannel) that string to some file called foo.java

This is basic, without any symbolic computation, but that should be enough and quick to have something up and running.

It depends if you want “that thing” (and people holding its leash) gaining power or not, isn’t it?
In additon, how are LLM made?…

include Std_disclaimer

I’m not sure, but maybe you can get it done using the the Format module with custom %a directives. (if that is the word). My example will use Fmt but is should not matter.

let nl ppf () = Fmt.pf ppf "\n"
let fmt_java_method ppf method_data = failwith "need to be done"

let fmt_java_methods ppf methods_data =
Fmt.list ~sep:nl fmt_java_method ppf fmt_java_method methods_data

let fmt_java_class ppf (name, methods_data) =
   Fmt.pf ppf {|
public class %s {
%a
}
|} name fmt_java_methods methods_data

I’m generating some C wrappers with moderate success like this. Here are two example of aux functions used:

let fmt_args ppf params =
  let fmt_arg ppf (attrs, _nodes) =
    let name = get_safe_name attrs in
    Fmt.pf ppf "value %s" name in
  Fmt.list ~sep:comma fmt_arg ppf params

let fmt_params ppf params =
  let nparams = List.length params in
  let fmt_name ppf (attrs, _nodes) =
    let name = get_safe_name attrs in
    Fmt.pf ppf "%s" name in
  Fmt.pf ppf "    CAMLparam%d(%a);" nparams (Fmt.list ~sep:comma fmt_name) params

I’m going on the simple way just manipulating strings with sprintf:

  • strings for java wrapping functions (main_first_sentence, main_last_sentence,…)
  • OCaml functions where a string represents a (verbose.because.of. the.long.path) java one line function with just placeholders for arguments (instance’s’ name, parameters : string, int…)

So java verbosity disappears in Ocaml functions.