Skip to content
Hara

Get started with the JVM

Use the JVM path when a Hara program needs Java libraries, Java classes, Maven builds, or the Truffle runtime. Hara remains Hara: JVM access is an explicit native flavor provided by the host, not a change to the portable language.

What this path gives you

The JVM host is useful for three related jobs:

  • running Hara on the Truffle implementation;
  • calling deliberately imported Java classes from Hara source;
  • contributing to the Java runtime and running its conformance tests.

Portable forms, persistent values, protocols, functions, atoms, iterators, promises, bytes, and strings keep their Hara behavior. Java reflection and class loading stay behind explicit provider and capability boundaries.

Install the prerequisites

Contributor builds require JDK 21 and Maven. Check both tools before building:

java -version
mvn -version

The output should show a JDK 21 runtime and a working Maven installation. A JRE alone is not enough for compiler and build tasks.

Build the Truffle runtime

From the Hara repository root, run:

mvn -f java/pom.xml -Ptruffle package

The build produces:

java/target/hara-truffle.jar

Use the checked-in launcher from the repository root for the normal CLI and REPL workflow:

./hara

Evaluate a form without entering the REPL:

./hara eval '(+ 19 23)'

Expected result:

42

Select the JVM flavor

A namespace opts into the JVM provider with (:flavor :jvm) and imports every Java class it uses:

(ns example.jvm
  (:flavor :jvm)
  (:import [java.lang String RuntimeException]
           [java.awt Point]))

Flavor selection belongs to that namespace. A required namespace does not inherit the caller's flavor, and selecting a flavor does not itself grant reflection, classpath mutation, compilation, or class-definition authority. The embedding host controls those capabilities separately.

Construct and call Java values

Only imported simple class names resolve. Construct a Java object with new:

(def point (new Point 10 20))

A qualified symbol such as String/valueOf resolves a static field or method:

(String/valueOf 42)

The dot form walks a value from left to right. A symbol reads a field and a list calls a method:

(. point x)
(. point (toString))

The JVM provider also supports its indexed-access extension with a one-item vector:

(. values [0])

Keep the distinction clear: ordinary Hara vectors, maps, sets, lists, and other persistent values continue to use Hara lookup and protocol behavior. They do not silently fall through to Java reflection.

Handle Java failures

Imported Java exception classes can appear in Hara catch forms. Reflection wrappers preserve the original Java cause so host failures remain inspectable. Unsupported classes, missing imports, denied reflection, and invalid member operations should fail deterministically rather than guessing another meaning.

When a call fails, check the boundary in this order:

  1. Is the namespace using (:flavor :jvm)?
  2. Is the class explicitly imported?
  3. Is the member name correct for the value or class?
  4. Has the host granted the required reflection or compiler capability?
  5. Is the code accidentally treating a persistent Hara value as a Java value?

Work with Java libraries

A library must be present on the runtime classpath before Hara can import its classes. Classpath changes are separate from ordinary reflection and should be made by the project or embedding host, not hidden inside portable source.

A practical project boundary is:

my-jvm-project/
  pom.xml
  project.edn
  workspace.edn
  src/
    example/
      app.hal

Use Maven to declare and resolve Java dependencies. Use Hara namespaces to express the program. Keep host-specific interop in a small boundary namespace so the rest of the project can remain portable and testable on another runtime.

For example:

(ns example.clock.jvm
  (:flavor :jvm)
  (:import [java.time Instant]))

(defn now-string []
  (. (Instant/now) (toString)))

Code outside example.clock.jvm can call now-string without needing to know how the host obtained the value.

Run the JVM tests

Run the Java test suite:

mvn -q -f java/pom.xml test

Run the focused Truffle L0 conformance test:

mvn -q -f java/pom.xml -Ptruffle \
  -Dtest=hara.truffle.HaraL0ConformanceTest test

Use the broader developer guide for test slices, native-image builds, tracing, and contributor troubleshooting.

Decide whether the JVM is the right host

Choose the JVM when Java interop is part of the product or when you are working on the Truffle implementation. Choose the Rust CLI for a small native runtime without a JVM dependency. Choose WebAssembly for browser embedding. The source language remains the same, but each host exposes a different set of explicit providers.

Continue

Read Native runtime flavors for the provider contract, Java and Rust APIs for host-facing APIs, and the developer guide for build and test workflows.