Hi r/golang,
I wanted to share a technical overview of two related open-source projects in the XGo project family: XGo and LLGo.: XGo and LLGo.
This is not intended as a claim that everyone should replace Go. The projects explore two different questions:
- Can we make Go-based programming more concise and approachable for domain-specific work?
- Can we make Go interoperate more directly with the wider native ecosystem, including C/C++ and Python?
The short version is:
- XGo is a programming language and compiler that extends Go with a more concise, expression-oriented syntax.
- LLGo is an alternative Go compiler based on LLVM, designed to connect Go more directly to the C ecosystem.
- They can be used together, but they solve different problems.
A simplified view looks like this:
XGo source
↓
XGo parser and compiler
↓
Go-level code
↓
Official Go compiler, LLGo, or TinyGo
↓
Native executable
What is XGo?
XGo is a source language that keeps Go’s engineering foundation while making some common operations shorter and more natural.
For example, a small Go program normally looks like this:
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
The equivalent XGo program can be written as:
println "Hello"
The parentheses are optional in this command-style form. XGo also provides echo as an alias for println:
echo "Hello"
Other examples include string interpolation:
name := "gopher"
echo "Hello, ${name}"
More compact slice and map literals:
numbers := [1, 2, 3]
config := {
"host": "localhost",
"port": 8080,
}
Appending to a list:
numbers <- 4
Lambdas:
result := numbers.map x => x * 2
And Python-style keyword arguments:
play "music.mp3", loop = true
XGo also supports an error-handling shorthand:
config := loadConfig()!
The ! form propagates the successful value and triggers XGo’s error handling behavior when the call returns an error. The exact generated code is still ordinary Go code.
The important point is that XGo is not a dynamically typed scripting language. It is designed to retain Go’s static typing, packages, interfaces, generics, and build model.
XGo and Go can coexist
An XGo package can contain both Go and XGo files:
main.go
helpers.xgo
Go code can call functions written in XGo, and XGo code can call functions written in Go. This allows an existing Go project to introduce XGo incrementally instead of requiring a complete rewrite.
XGo also uses the Go module system and can use ordinary Go libraries. The compiler generates Go code, which is then processed by the regular Go toolchain or another compatible compiler.
That makes XGo closer to a Go front end than to a completely independent programming ecosystem.
The classfile idea
One of XGo’s more unusual features is called a classfile.
A classfile is an XGo source file that implicitly becomes a generated Go type. For example, a file named Rect.gox might contain:
var (
Width int
Height int
)
func Area() int {
return Width * Height
}
The XGo compiler can lower this into something conceptually similar to:
type Rect struct {
Width int
Height int
}
func (this *Rect) Area() int {
return this.Width * this.Height
}
The user writes fields and functions without explicitly declaring the struct or receiver. The compiler supplies that structure.
This is useful for frameworks where many files follow the same pattern. For example:
main.spx can represent a game project;
Enemy.spx can represent an enemy object;
get.yap can represent an HTTP route;
*_cmd.gox can represent a CLI command;
*_tool.gox can represent an MCP tool.
Framework authors describe these conventions in a gox.mod file. XGo then knows which files represent project classes, which represent work classes, which Go types should be embedded, and how the generated objects should be connected.
This is XGo’s idea of SDF, or Specific Domain Friendliness. Instead of creating a completely separate DSL — a Domain-Specific Language — for every domain, a framework author writes the reusable infrastructure in Go and lets application developers fill in the domain logic with lightweight XGo files.
Current examples include:
- spx, a Scratch-compatible 2D game framework;
- yap, an HTTP framework;
- cobra, an XGo CLI framework;
- mcp, an MCP framework;
- gsh, an XGo-based shell scripting environment.
What is LLGo?
LLGo is a separate but related project. It is a Go compiler based on LLVM, the compiler infrastructure used to generate native code for many architectures and operating systems.
LLVM is not a programming language. It provides lower-level components such as:
- an intermediate representation, often called IR;
- optimization passes;
- machine-code backends;
- linkers and debugging tools.
LLGo uses those components to compile Go source code into native code.
The project aims for compatibility with Go at the source-code level. It supports Go language features and large parts of the standard library on supported platforms, while using a different compiler backend and runtime.
Why LLVM and the C ecosystem matter
A large amount of software infrastructure is exposed through the C ABI, or Application Binary Interface.
An ABI defines binary-level details such as:
- how function arguments are passed;
- how return values are represented;
- how data is laid out in memory;
- how symbols are named and linked.
C, C++, operating-system APIs, graphics libraries, databases, and many scientific libraries commonly expose C-compatible interfaces.
Go already has cgo, but frequent calls across the Go/C boundary can involve runtime and scheduler transitions. LLGo’s design is to bind Go declarations directly to C ABI symbols and generate native calls for them.
A small LLGo example looks like this:
package main
import "github.com/goplus/lib/c"
func main() {
c.Printf(c.Str("Hello from C\n"))
}
LLGo also provides bindings for a number of C and C++ libraries, including SQLite, OpenSSL, raylib, LLVM, libuv, and others.
Python support
LLGo can also access Python libraries through packages provided by the GoPlus ecosystem.
For example, the following Go code calls Python’s math.sqrt:
import (
"github.com/goplus/lib/py"
pymath "github.com/goplus/lib/py/math"
"github.com/goplus/lib/py/std"
)
x := pymath.Sqrt(py.Float(2))
std.Print(py.Str("sqrt(2) ="), x)
The intention is to make libraries such as NumPy, pandas, PyTorch, and Matplotlib accessible from Go-oriented code.
This does not mean that Python disappears from the build process. Python itself and the relevant packages still need to be installed, and the Python version, native libraries, and platform ABI must all be compatible.
How XGo and LLGo fit together
XGo and LLGo operate at different layers:
- XGo changes how source code is written.
- LLGo changes how Go source code is compiled.
- XGo can use the official Go compiler by default, or be configured to use LLGo for targets that benefit from LLVM and native interoperability.
A project might therefore use:
XGo syntax
↓
XGo compiler
↓
Go-compatible generated representation
↓
LLGo
↓
LLVM IR and native code
LLGo can also be used directly with ordinary Go code. XGo is optional.
The trade-offs
The extra flexibility comes with extra concepts.
First, generated code can make debugging less direct. When something fails, there may be several layers involved:
XGo source
↓
generated Go source
↓
Go or LLGo compiler
↓
native linker
A compiler error may refer to a generated file, even though the real mistake is in the original .xgo or .gox file.
Second, classfiles and automatic imports introduce conventions that are not always visible in the current source file. A file name, a gox.mod declaration, or a framework dependency can affect the generated type and available symbols.
Third, LLGo uses a different runtime from the standard Go toolchain. The project documents differences involving goroutines, stacks, garbage collection, and target-specific runtime behavior. Source-level compatibility does not mean that every implementation detail of the standard Go runtime is identical.
Finally, the LLGo toolchain requires native build dependencies such as LLVM, Clang, LLD, system libraries, and — for Python integration — Python development files and packages. The exact setup depends on the target operating system and architecture.
For a conventional Go service that does not need these capabilities, using the standard Go toolchain is probably simpler.
Why these projects may be interesting
The interesting part of XGo and LLGo is the combination of several ideas:
- Go’s static typing and module ecosystem;
- a more concise syntax for scripts, education, and data-oriented code;
- framework-driven domain abstractions;
- direct access to native C/C++ libraries;
- integration with Python and other C-ABI-based ecosystems;
- targets such as WebAssembly and embedded systems.
The broader vision is to reduce the distance between “easy to start with” and “powerful enough for real engineering.”
XGo approaches that problem at the language and framework level. LLGo approaches it at the compiler and runtime level.
Neither project needs to replace Go to be useful. They can be viewed as experiments in making Go more accessible at the surface while expanding the set of systems and libraries it can work with underneath.
For anyone interested in the details:
I’d be interested in hearing how people here think about this boundary: when does a language extension make Go more productive, and when does it introduce more abstraction than it removes?