# Frege: a Haskell for the JVM, and What It Costs to Adopt

> Frege compiles a lazy, purely functional language to Java classes and lets Frege and Java call each other. The compiler is mature, the release cadence is not, and the search traffic around the name mostly belongs to a nineteenth-century logician.

**Frege/frege** — Frege is a Haskell for the JVM. It brings purely functional programing to the Java platform.

- Repository: https://github.com/Frege/frege
- Website: https://github.com/Frege/frege/wiki/_pages
- Stars: 3,711 · Forks: 148
- Language: Frege
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/frege-frege

## What Frege Solves, and for Whom

Frege exists for two audiences that the README names explicitly. The first is the Java programmer who wants a purely functional language without leaving the JVM: Frege compiles to Java, runs on the JVM, and uses any Java library. The second is the Haskell programmer who wants to reuse existing skills on the JVM. The README describes Frege as a Haskell for the JVM, and says most idiomatic Haskell code will run in Frege.

The problem it addresses is narrower than "functional programming on the JVM". Other JVM languages add lambdas and immutable collections. Frege adds non-strict evaluation, global type inference, and a type system that tracks side effects, so a function's purity is visible to the compiler rather than being a convention in the source. That is the part that cannot be bolted onto Java incrementally, and it is the reason to consider Frege rather than a library.

The README frames the design as islands of purity in a sea of imperative code. A pure function such as the greeting example is stateless and free of side effects, so it is threadsafe and its results may be automatically cached. The main function is impure and has type IO (), and the type system guarantees that any caller of main must also be of some IO type. Impurity percolates up the call chain instead of disappearing at a boundary.

## How the Compiler, the Type System and Java Interop Fit Together

Frege source files use the .fr extension. The Makefile declares a suffix rule for .fr to .class, and the README states that the Hello example compiles to Hello.class and Hello.java with a regular Java main method that can be started the usual way. So the compiler emits both bytecode and Java source, and the generated Java is a readable artifact rather than an internal detail.

Interop runs in both directions. A pure Frege function becomes a static Java method: the README shows that greeting compiles to a method with the signature public static String greeting(String ...). Calling Java from Frege is possible but not free. The README is direct about this: when calling Java from Frege, you have to declare the Java types in rigid Frege terms in order to preserve purity, thread safety, and lazy evaluation. That is the real cost of the interop story. The boundary is where the language guarantees are re-established by hand, and a wrong declaration there is a correctness problem, not a style problem.

Purity is not only a safety property. The README says the compiler can potentially use purity information for optimizations such as pre-calculation, deferred execution, parallel execution, caching, and elimination of common subexpressions. The word potentially matters. The README does not quantify any of these, and no benchmark is given.

Laziness shows up in ordinary code. The cosine fixpoint example uses iterate to build an infinite list of values and takes the head of a filtered pair list. The README notes that the = signs denote definitions, not assignments, and that there are no assignments in Frege. That single sentence explains most of what surprises a Java developer reading the examples.

## Building and Running Frege from Source

The repository does not present a package-manager install. The top-level layout has a Makefile, build-instructions.md, and a scripts directory, so a source build is the documented route. The Makefile comments state that the standard distribution needs a Java 1.7 or higher JDK, and that YACC should be a BSD compatible yacc. On Ubuntu the comment suggests byacc-j, and it says byacc and pbyacc should also work.

The Makefile is also explicit about Windows, with a parenthetical that is not flattering, and it tells you to change the path separator characters. It offers two mechanisms for selecting the right Java: put the JDK7 in your PATH after other JDKs and make java7 a symbolic link to the JDK7 java binary, or on UNIX use an alias.

```bash
alias fmake='make JAVA="/path/to/jdk7/java" '
```

With that alias in place, the build is driven by make, and the default JAVA variable is java -Dfrege.javac=internal. The compiler target is 1.8. Before running anything, read build-instructions.md, which the repository carries at the top level and which the Makefile comments do not replace.

Once the compiler is built, the README points to an Online REPL at try.fregelang.org for trying expressions, and gives a one-line example that computes pythagorean triples below 10 with a list comprehension:

```frege
[ (x,y,z) | x <- [1..10], y <- [x..10], z <- [x..10], x*x + y*y == z*z ]
```

The README says you should see a list of triples containing the solutions (3, 4, 5) and (6, 8, 10). For a compiled program, the Hello module is the smallest complete unit: it declares module Hello, defines greeting, and defines main args, which the README says compiles to Hello.class and Hello.java with a Java main method. The examples directory contains larger programs, including Brainfuck.fr, Concurrent.fr, Grep.fr and a set of Project Euler solutions, which is a better starting point than the README snippets once the toolchain works.

## Release Artifacts, Maintenance and the Upgrade Question

This is where Frege is weakest, and it is worth stating plainly. The most recent release listed is 3.25alpha, a pre-release dated 2018-05-10. Before that, 3.24public is dated 2018-04-08 and is described as the latest builds of Frege 3.24.xxx for Java 1.8 and higher. The 3.24alpha release is from 2016. The repository itself is not archived, and the last push was on 2026-07-11, so the source tree is not frozen. But the release channel has been quiet since 2018, and the newest tagged artifact is explicitly an alpha.

That combination shapes the upgrade story. A team that adopts Frege is choosing between an alpha pre-release and a 2018 public build, or building master from source. The README does not document rollback, and it does not describe a version compatibility policy between compiler releases and generated Java. The Makefile's TARGET = 1.8 and the requirement for a Java 1.7 or higher JDK tell you the floor, not the supported range.

The practical consequence is that Frege is not a dependency you pin and forget. If you build from master, your compiler version is whatever commit you built, and reproducing that build later means reproducing the JDK and byacc environment too. The repository's Travis configuration and Makefile targets for build/test and build/regression suggest the project does have a test path, but the README does not describe a release validation process a downstream user can rely on.

## Licence and the Cost of Shipping Frege Code

The repository carries a LICENSE.txt at the top level, but the licence identifier given for the project is NOASSERTION, which means no standard licence was detected. The README does not state a licence, and nothing in the repository's public description lays out the terms under which the compiler or the generated Java may be redistributed.

That matters more here than in a typical library, because Frege's output is Java source and Java bytecode. If you ship a Frege-compiled artifact, you are shipping generated Java derived from your source, and the compiler's own licence governs the compiler, not necessarily your output. The repository does not answer whether generated code carries any obligation.

Read LICENSE.txt directly and have someone qualified review it before shipping Frege-compiled code in a product. This is not a legal opinion, and the absence of a recognized identifier is exactly the case where reading the file beats trusting a scanner.

## When Frege Is the Wrong Choice

The clearest failure mode is the Java interop boundary. The README states that calling Java from Frege requires you to declare the Java types in rigid Frege terms to preserve purity, thread safety and lazy evaluation. If your work is mostly gluing Java libraries together, most of your code sits on that boundary, and you pay the declaration cost on nearly every call while getting little of the type system's benefit. Frege is at its best when the pure core is large and the impure shell is thin, which is the opposite of a typical integration-heavy service.

Laziness is the second constraint. The cosine example builds an infinite list and relies on taking only the head. Java developers coming from strict evaluation will not predict where work happens, and the README's own framing, that the code is most likely incomprehensible for a Frege/Haskell newcomer at first, is honest about the learning curve. Debugging a lazily evaluated program is a different skill from debugging a strict one, and the repository does not describe tooling for it.

Third, if you need a language with a current release train, Frege is not it. The newest listed release is an alpha from 2018. A team that requires vendor support, a security response process, or a documented migration path between versions will not find one here.

Finally, the name is a practical obstacle. The related searches for this project are dominated by Gottlob Frege: Begriffsschrift, the sense and reference distinction, the Frege-Geach problem, pronunciation, and quotes. Searching for help returns philosophy. That is a real cost when a developer hits an error at 2 a.m.

## How Frege Differs from Scala and Clojure

The obvious JVM alternatives are Scala and Clojure, and the difference is not syntax. Scala is a strict, statically typed language that mixes functional and object-oriented style and interoperates with Java without a purity declaration layer. Clojure is a dynamically typed Lisp with persistent data structures and strict evaluation, and it embraces host interop directly. Frege is the only one of the three that is lazy by default and that carries purity in the type system, with side effects confined to IO.

That difference cuts both ways. Scala and Clojure let you call a Java method and move on; Frege makes you declare the Java type in Frege terms first. Scala and Clojure have release trains and package distribution; Frege's newest listed artifact is a 2018 alpha. In exchange, Frege gives you global type inference without annotations, non-strict evaluation, and a compiler that can reason about which expressions are pure, which the README says enables optimizations such as pre-calculation and caching.

If you want functional programming on the JVM with a large ecosystem and current releases, Scala or Clojure is the safer pick. If what you actually want is Haskell semantics with access to Java libraries, and you are willing to build the toolchain yourself, Frege is the closest thing on this platform.

## Conclusion

Adopt Frege if you have a JVM codebase where a pure, lazily evaluated core with global type inference earns its keep, and you accept building the compiler from source with a Java 1.7 or higher JDK, byacc, and make. Do not adopt it if you need a documented upgrade path, current release artifacts, or a language whose Google results are not dominated by Gottlob Frege. Verify first that the 3.25alpha pre-release jars still match the master branch you intend to build, and read build-instructions.md before running make.

## FAQ

### Does Frege run on the JVM?

Yes. The README describes Frege as a Haskell for the JVM, and says it compiles to Java, runs on the JVM, and uses any Java library. The compiler itself is built with a Java 1.7 or higher JDK according to the Makefile comments.

### Can I call Frege code from Java?

Yes, and the README gives an example. The Hello module compiles to Hello.class and Hello.java, and the greeting function becomes a method with the signature public static String greeting(String ...). The README links to a wiki page on calling Frege code from Java.

### What do I need to build the Frege compiler from source?

The Makefile comments state that the standard distribution needs a Java 1.7 or higher JDK and a BSD compatible yacc. On Ubuntu the comment suggests byacc-j, and says byacc and pbyacc should also work. The repository also carries build-instructions.md at the top level.

### What is the latest Frege release?

The most recent release listed is 3.25alpha, described as pre-released jars for 3.25 and dated 2018-05-10. The latest public build listed is 3.24public, dated 2018-04-08 and described as builds of Frege 3.24.xxx for Java 1.8 and higher.

### Is Frege still maintained?

The repository is not archived and the last push was on 2026-07-11, so the source tree is being changed. The release channel is a separate matter: the newest listed release artifact is an alpha from 2018. The README does not describe a release cadence or a support policy.

## Sources

- [Frege/frege on GitHub](https://github.com/Frege/frege)
- [Issues](https://github.com/Frege/frege/issues)
- [Project website](https://github.com/Frege/frege/wiki/_pages)
- [README](https://github.com/Frege/frege/blob/master/README.md)
- [Releases](https://github.com/Frege/frege/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/frege-frege
