Languages that compile to Lua


Since Lua 5.4.0 is out, and that made me really happy, I decided to dedicate a small post to languages that compile to Lua.


Moonscript has been around for a while (one of the reasons I forgot to add it to this post in my initial publication) and is stable and used in the industry, it is probably the most popular compile to Lua language out there. I believe that a large part of is powered by moonscript and NginX. Moonscript feels like CoffeeScript but for Lua. If you pair it with the Lapis web framework by the same author, you have a delightful setup to launch your next web application.

MoonScript is a dynamic scripting language that compiles into Lua. It gives you the power of one of the fastest scripting languages combined with a rich set of features.

Source: MoonScript, a language that compiles to Lua

Example of Moonscript

class Thing
  name: "unknown"

class Person extends Thing
  say_name: => print "Hello, I am #{@name}!"

with Person!
  .name = "MoonScript"

Moonscript is built with Lua and Moonscript itself (almost in equal parts).


I believe Fennel is the most popular new compile to Lua language current out there. There are a lot of people being Fennel developers first and Lua developers second. It looks quite neat, it has a Lisp flavour in its syntax and macro system but it is not a Common Lisp, its semantics conform to Lua semantics in most places.

  • Full Lua compatibility: Easily call any Lua function or library from Fennel and vice-versa.
  • Zero overhead: Compiled code should be just as or more efficient than hand-written Lua.
  • Compile-time macros: Ship compiled code with no runtime dependency on Fennel.
  • Embeddable: Fennel is a one-file library as well as an executable. Embed it in other programs to support runtime extensibility and interactive development.

Source: the Fennel programming language

It is quite neat and like Lua, it can run on a small ESP32 all the way to beefy server in a datacenter running OpenResty.

Examples of Fennel

The sample below is using Löve which is a game development library for Lua.

;; Sample: read the state of the keyboard and move the player accordingly
(local dirs {:up [0 -1] :down [0 1] :left [-1 0] :right [1 0]})

(each [key delta (pairs dirs)]
  (when (love.keyboard.isDown key)
    (let [[dx dy] delta
          [px py] player
          x (+ px (* dx player.speed dt))
          y (+ py (* dy player.speed dt))]
      (world:move player x y))))

Fennel is mostly built in Lua.


Urn is also a Lisp-like language for Lua. While Fennel is Lua in Lisp clothes, Urn is more into the Lisp-family being influenced by Common Lisp and Clojure.

  • A minimal¹ Lisp implementation, with full support for compile time code execution and macros.
  • Support for Lua 5.1, 5.2 and 5.3. Should also work with LuaJIT.
  • Lisp–1 scoping rules (functions and data share the same namespace).
  • Influenced by a whole range of Lisp implementations, including Common Lisp and Clojure.
  • Produces standalone, optimised Lua files: no dependencies on a standard library.

Source: Urn: A Lisp implementation for Lua | Urn

Example of Urn

Who doesn’t like pattern matching? All languages should have it by now…

> (case '("x" (foo 2 3))
.   [(string?  @ ?x) (.. "Got a string " x)]
.   [("x" (foo . ?x)) (.. "Got some remaining values " (pretty x))])
out = "Got some remaining values (2 3)"

Urn compiles to Lua but it is written mostly in Lua and Urn itself.

Amulet ML

Amulet ML is a functional language in the ML tradition (I love ML).

  • Generalised Algebraic Data Types (GADTs),
  • Multi-parameter type classes with associated types and functional dependencies, including support for quantified constraints,
  • Type functions supporting dependent kinds, and equality constraints,
  • Rank-N, impredicative types via Quick Look: ∀ quantifiers can appear anywhere in a type, not only the top-level,
  • Principled, extensible records via row polymorphism support inference of principal types.

Source: Amulet ML

Example of Amulet ML

let map f = function
| [] -> []
| Cons (x, xs) -> Cons (f x, map f xs)

let filter p xs = [ x | with x <- xs, p x ]
  • Minimalist syntax: semicolons, in and other noise is entirely optional.
  • Pattern matching and guards make writing code more intuitive.
  • List comprehensions are a short and sweet way of expressing complex computations on lists.

Be aware that even though Amulet ML compiles to Lua, it is written basically in Haskell with some bits in SML and OCaml.

Did you enjoyed reading this content? Want to support me?

You can buy me a coffee at ko-fi.

Comments? Questions? Feedback?

You can reach out to me on Twitter, or Mastodon, Secure Scuttlebutt, or through WebMentions.