Skip to content
Oeiuwq Faith Blog OpenSource Porfolio

alaingilbert/agl.json
{
"createdAt": "2025-06-10T04:13:38Z",
"defaultBranch": "master",
"description": "AGL (AnotherGoLang)",
"fullName": "alaingilbert/agl",
"homepage": "https://alaingilbert.github.io/agl/",
"language": "Go",
"name": "agl",
"pushedAt": "2025-11-16T01:29:11Z",
"stargazersCount": 104,
"topics": [],
"updatedAt": "2025-11-18T21:10:21Z",
"url": "https://github.com/alaingilbert/agl"
}

AGL

AGL is a language that compiles to Go.
It uses Go’s syntax, in fact its lexer/parser is a fork of the original Go implementation, with a few modifications
The main differences are:

  • Functions return only a single value. This makes it possible to use types like Option[T] and Result[T], and to support automatic error propagation via an operator.
  • To make returning multiple values easy, a Tuple type has been introduced. For example: Result[(u8, string, bool)]
  • AGL can be used as a scripting language

Notable change: number types are int i8 i16 i32 i64 uint u8 u16 u32 u64 f32 f64

  • Tuple / Enum / Set
  • Immutable variables/structs by default
  • Error propagation operators (? for Option[T] / ! for Result[T])
  • Concise anonymous function with type inferred arguments (other := someArr.Filter({ $0 % 2 == 0 }))
  • Array built-in Map/Reduce/Filter/Find/Sum methods
  • Operator overloading
  • Compile down to Go code
  • VSCode extension & LSP (language server protocol)
  • SourceMap
  • Shell “shebang” support
go build // Build the "agl" executable
./agl main.agl // Output Go code in stdout
./agl run main.agl // Run the code directly and output the result in stdout
./agl build main.agl // Create a main.go file
package main
func getInt() int! { // `int!` means the function return a `Result[int]`
return Ok(42)
}
func intermediate() int! {
num := getInt()! // Propagate 'Err' value to the caller
return Ok(num + 1)
}
func main() {
num := intermediate()! // crash on 'Err' value
print(num)
}
package main
func maybeInt() int? { // `int?` means the the function return an `Option[int]`
return Some(42)
}
func intermediate() int? {
num := maybeInt()? // Propagate 'None' value to the caller
return Some(num + 1)
}
func main() {
num := intermediate()? // crash on 'None' value
print(num)
}
package main
type Person struct { Name string }
func (p Person) MaybeSelf() Person? {
return Some(p)
}
func main() {
bob := Person{Name: "bob"}
bob.MaybeSelf()?.MaybeSelf()?.MaybeSelf()?
}

If let Some(val) := ... { to use a Option[T]/Result[T] value safely

Section titled “If let Some(val) := ... { to use a Option[T]/Result[T] value safely”

This pattern works with any of Ok|Err|Some

package main
func maybeInt() int? { Some(42) } // Implicit return when a single expression is present
func main() {
if let Some(num) := maybeInt() {
print(num)
}
}

This pattern works with any of Ok|Err|Some

package main
func maybeInt() int? { Some(42) } // Implicit return when a single expression is present
func main() {
guard let Some(num) := maybeInt() else { return } // can use guard to unwrap a value
guard num < 100 else { return } // can use guard as a reverse if
assert(num == 42)
}
package main
func getInt() int! { Ok(42) }
func maybeInt() int? { Some(42) }
func main() {
match getInt() {
case Ok(num):
print("Num:", num)
case Err(err):
print("Error:", err)
}
match maybeInt() {
case Some(num):
print("Num:", num)
case None:
print("No value")
}
}

or_break/or_continue will break/continue on a None/Err value

package main
import "time"
func test(i int) int? {
if i >= 2 {
return None
}
return Some(i)
}
func main() {
for i in 0..10 {
res := test(i) or_break // `res` has type `int`
print(res) // will print the value `0` and `1`
time.Sleep(time.Second)
}
}

Arguments are mapped into $0|$1
In this example, x becomes $0 when using the short form.

LangExpression
Goutils.Filter(arr, func(x int) bool { return x % 2 == 0 })
AGLarr.Filter({ $0 % 2 == 0 })

Since the function is expected to return something and there is only one expression, it will be returned automatically.

package main
type Person struct {
Name string
Age int
}
func main() {
arr := []int{1, 2, 3, 4, 5}
sum := arr.Filter({ $0 % 2 == 0 }).Map({ $0 + 1 }).Sum()
assert(sum == 8)
p1 := Person{Name: "foo", Age: 18}
p2 := Person{Name: "bar", Age: 19}
people := []Person{p1, p2}
names := people.Map({ $0.Name }).Joined(", ")
sumAge := people.Map({ $0.Age }).Sum()
assert(names == "foo, bar")
assert(sumAge == 37)
}

You can also name the arguments using the following syntax:

names := people.Map(|person| { person.Name }).Joined(", ")

If a single statement is present in the lambda body, the curly brace can be omitted:

names := people.Map(|person| person.Name).Joined(", ")
package main
type IpAddr enum {
V4(u8, u8, u8, u8)
V6(string)
}
func main() {
home := IpAddr.V4(127, 0, 0, 1)
// match branches must be exhaustive
match home {
case .V4(a, b, c, d):
print(a, b, c, d)
case .V6(addr):
print(addr)
}
// if-let can be used to pattern match
if let IpAddr.V4(a, b, c, d) := home {
print(a, b, c, d)
}
// guard-let can also be used to pattern match
guard let IpAddr.V4(a, b, c, d) := home else { return }
print(a, b, c, d)
}
package main
type IpAddr enum {
v4(u8, u8, u8, u8)
v6(string)
}
func main() {
// enum values can be destructured
addr1 := IpAddr.v4(127, 0, 0, 1)
(a, b, c, d) := addr1
// tuple can be destructured
tuple := (1, "hello", true)
(e, f, g) := tuple
print(a, b, c, d, e, f, g)
}
package main
func main() {
for el in []int{1, 2, 3} {
print(el)
}
for el in set[int]{1, 2, 3} {
print(el)
}
for (k, v) in map[string]u8{"a": 1, "b": 2, "c": 3} {
print(k, v)
}
for (a, b, c) in []!(int, string, bool){(1, "foo", true), (2, "bar", false)} {
print(a, b, c)
}
for el in 0..3 { // 0 1 2
print(el)
}
for el in 0..=3 { // 0 1 2 3
print(el)
}
for el in (0..3).Rev() { // 2 1 0
print(el)
}
for c in "hello" {
print(string(c))
}
for (i, c) in "hello".Enumerated() {
print(i, string(c))
}
for el in []int{1, 2, 3} where el % 2 == 0 { // for-in can have an optional where clause
print(el)
}
}
package main
func main() {
name := "bob"
age := 42
print(t"{name} is {age} years old") // bob is 42 years old
}
package main
type Person struct {
Name string
Age int
}
func (p Person) == (other Person) bool {
return p.Age == other.Age
}
func main() {
p1 := Person{Name: "foo", Age: 42}
p2 := Person{Name: "bar", Age: 42}
assert(p1 == p2) // People of the same age are obviously equal!
}
package main
func (v agl.Vec[T]) Even() []T {
out := make([]T, 0)
for _, el := range v {
if el % 2 == 0 {
out = append(out, el)
}
}
return out
}
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8}
res := arr.Even().Filter({ $0 <= 6 }).Map({ $0 + 1 })
// ^^^^^^ new method available
print(res) // [3 5 7]
}

Methods can have generic type parameters

func (v agl.Vec[T]) MyMap[R any]!(clb func(T) R) []R {
mut out := make([]R, 0)
for _, el := range v {
out = append(out, clb(el))
}
return out
}

You can also extend for a specific type of vector

func (v agl.Vec[string]) MyJoined(sep string) string {
return strings.Join(v, sep)
}

assert and assertEq will not be part of the release code.
precondition will be part of both debug and release code.

package main
func main() {
a := 42
assert(a == 42)
assert(a == 42, "some message")
assertEq(a, 42) // assertEq gives a better error message containing the values at runtime
assertEq(a, 42, "some message")
precondition(a == 42)
precondition(a == 42, "some message")
}
package main
type Point struct {
X, Y int
}
// Point implements ExpressibleByStringLiteral
func (Point) FromStringLit(s string) Point {
(p1, p2) := s.Cut(":")?
return Point{X: p1.Int()?, Y: p2.Int()?}
}
func PrintPoint(p Point) {
print(p.X, p.Y)
}
func main() {
PrintPoint("1:2") // a string literal can be used to create a Point
PrintPoint(Point{X: 1, Y: 2})
}
package main
type Container struct {
Val int
}
/*
a := c1 ?? &Container{Val: 42}
// is equivalent to -->
a := c1
if a == nil {
a = &Container{Val: 42}
}
*/
func main() {
var c1 *Container
c2 := &Container{Val: 1}
a := c1 ?? &Container{Val: 42}
print(a.Val) // 42
b := c2 ?? &Container{Val: 42}
print(b.Val) // 1
}
package main
type Container struct {
mut Val int
}
/*
c1?.Val = 42
// is equivalent to -->
if c1 != nil {
c1.Val = 42
}
*/
func main() {
var mut c1 *Container
mut c2 := &Container{}
c1?.Val = 42
c2?.Val = 42
print(c1) // <nil>
print(c2) // &{42}
}
package main
import "agl/os"
func main() {
os.WriteFile("test.txt", []byte("test"), 0755)!
by := os.ReadFile("test.txt")!
print(string(by))
}

Here is a guide on how to use external libraries
And here is a simple example

package main
import (
"agl/net/http"
"agl/io"
)
func main() {
req := http.NewRequest(http.MethodGet, "https://google.com", None)!
c := http.Client{}
resp := c.Do(req)!
defer resp.Body.Close()
by := io.ReadAll(resp.Body)!
print(string(by))
}
#!/usr/bin/env agl run
package main
func main() {
print("Hello AGL!")
}
Terminal window
$ chmod +x hello.agl
$ ./hello.agl
Hello AGL!