Examples

Examples

Complete, runnable programs demonstrating Sun. Every example below is compiled and executed in CI, and the source shown here is the exact source in the examples/ (opens in a new tab) folder.

Hello

The smallest Sun program: main prints a greeting via the stdlib's println.

export SUN_PATH=".:../../build"
sun --compile -o main main.sun
./main
# Hello, Sun!

Source

main.sun
using sun;
 
function main() void {
  println("Hello, Sun!");
}
 
manifest {
    moons: ["stdlib.moon"]
}

Classes

Classes are stack-allocated value types with methods and an init constructor. Class values are passed by ref (Sun never copies them implicitly). This Point computes the Manhattan distance between two points.

./build.sh
./main

Source

main.sun
using sun;
 
// Classes are stack-allocated value types. They are passed by `ref` and
// carry methods, including an `init` constructor.
class Point {
  public var x: i32;
  public var y: i32;
 
  public function init(x_: i32, y_: i32) {
    this.x = x_;
    this.y = y_;
  }
 
  // Manhattan distance to another point. Class arguments are passed by ref.
  public function manhattan(other: ref Point) i32 {
    var dx = this.x - other.x;
    if (dx < 0) {
      dx = -dx;
    }
    var dy = this.y - other.y;
    if (dy < 0) {
      dy = -dy;
    }
    return dx + dy;
  }
}
 
function main() i32 {
  var origin = Point(0, 0);
  var p = Point(3, 4);
 
  var dist = p.manhattan(origin);  // 7
  println(dist);
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}

Interfaces

Interfaces declare behaviour that classes can implements. A function taking ref Drawable dispatches dynamically to the concrete type at runtime, so render draws a Circle or a Square without knowing which it holds.

./build.sh
./main

Source

main.sun
using sun;
 
// An interface declares behaviour that classes can implement. Functions can
// accept `ref Interface` and dispatch dynamically to the concrete type.
interface Drawable {
  public function draw() void;
}
 
class Circle implements Drawable {
  public var radius: f64;
 
  public function init(r: f64) {
    this.radius = r;
  }
 
  public function draw() void {
    println("Drawing circle with radius:");
    println(this.radius);
  }
}
 
class Square implements Drawable {
  public var side: f64;
 
  public function init(s: f64) {
    this.side = s;
  }
 
  public function draw() void {
    println("Drawing square with side:");
    println(this.side);
  }
}
 
// `shape` is dispatched dynamically based on its concrete type.
function render(shape: ref Drawable) void {
  shape.draw();
}
 
function main() i32 {
  var c = Circle(5.0);
  var s = Square(10.0);
 
  render(c);
  render(s);
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}

Error Handling

Functions that can fail declare an error type with the , IError suffix and signal failure with throw. Errors are real exceptions: a throw unwinds the stack to the nearest matching catch. Any class implementing IError can be thrown — here the standard library's DivisionByZeroError.

./build.sh
./main

Source

main.sun
using sun;
 
// A function that can fail declares an error type after its return type with
// the `, IError` suffix, and signals failure with `throw`. The thrown value is
// any class implementing `IError` (the standard library provides several).
function divide(a: i32, b: i32) i32, IError {
  if (b == 0) {
    throw DivisionByZeroError();
  }
  return a / b;
}
 
function main() i32 {
  // A successful call needs no special handling.
  try {
    var ok = divide(10, 2);  // 5
    println(ok);
  } catch (e: IError) {
    println("unexpected error");
  }
 
  // A failing call unwinds to the nearest matching catch.
  try {
    var bad = divide(10, 0);  // throws DivisionByZeroError
    println(bad);
  } catch (e: IError) {
    println("caught division by zero");
  }
 
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}

Class Destructors

Sun runs a class's deinit method automatically when a value goes out of scope, giving deterministic cleanup with no garbage collector. Here foo is destroyed at the end of main.

./build.sh
./main

Source

main.sun
using sun;
 
class Foo {
  public function init() {}
 
  function deinit() void {
    println("Foo deinit was called.");
  }
}
 
function main() void {
  var foo = Foo();
  println("Exiting main, foo will go out of scope and deinit will be called.");
}
 
manifest {
    moons: ["stdlib.moon"]
}

Lambdas & Closures

Lambdas are anonymous functions that capture variables from their enclosing scope. Here scale closes over multiplier and is applied to each value from 1 to 5, summing to 150.

./build.sh
./main

Source

main.sun
using sun;
 
function main() i32 {
  var multiplier: i32 = 10;
 
  // A lambda captures `multiplier` from the enclosing scope by closure.
  var scale = lambda (x: i32) i32 {
    return x * multiplier;
  };
 
  var total: i32 = 0;
  for (var i: i32 = 1; i <= 5; i = i + 1) {
    total = total + scale(i);  // 10 + 20 + 30 + 40 + 50
  }
 
  println(total);  // 150
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}

Modules & Transitive Moons

Sun compiles reusable libraries into .moon files. Dependencies are transitive at the bitcode level but opaque at the symbol level: main sees moon1, but not the moon2/moon3 symbols that moon1 pulls in. The chain main -> moon1 -> moon2 -> moon3 computes 1 + 2 + 3 = 6.

The compiled moon1.moon file contains the bitcode of moon2 and moon3.

./build.sh
./main

Source

main.sun
using sun;
 
function main() void {
  println(moon1.moon1());
}
 
manifest {
    moons: ["stdlib.moon", "moon1.moon"]
}
moon1/entry.sun
public module moon1 {
  public function moon1() i32 {
    return 1 + moon2.moon2();
  }
}
 
manifest {
    suns: []
    moons: [{ path: "moon2.moon"}]
}
moon2/entry.sun
public module moon2 {
  public function moon2() i32 {
    return 2 + moon3.moon3();
  }
}
 
manifest {
    suns: []
    moons: ["moon3.moon"]
}
moon3/entry.sun
public module moon3 {
  public function moon3() i32 {
    return 3;
  }
}
 
manifest {
    suns: []
    moons: []
}

TCP Connection

A raw TCP client/server built on the standard library's TcpListener and TcpStream. The listener binds to 127.0.0.1:8080 and prints whatever it receives; the talker connects and sends a message.

Build both executables:

./build.sh

Run the listener first, then the talker in a second terminal:

./listener   # terminal 1
./talker     # terminal 2

Source

listener.sun
// Listener: accepts one connection and prints received message
using sun;
 
function main() i32 {
  var listener = TcpListener();
  listener.bind_loopback(8080);
  listener.listen(1);
  println("Listening on 127.0.0.1:8080...");
 
  var client = listener.accept();
  println("Client connected");
 
  var alloc = make_heap_allocator();
  var buf = ContiguousBuffer<u8>(alloc, 256);
 
  while (true) {
    var n = client.recv(buf);
    if (n <= 0) {
      println("Connection closed");
      break;
    }
    try {
      var str = String(alloc, buf, n);
      println(str);
    } catch (e: IError) {
      println("Failed to create string");
    }
  }
 
  client.close();
  listener.close();
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}
talker.sun
// Talker: connects and sends a message
using sun;
 
function main() i32 {
  var stream = TcpStream();
  stream.connect_local(8080);
  println("Connected to 127.0.0.1:8080");
 
  stream.send_str("Hello from talker!");
  println("Message sent");
 
  stream.close();
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}

HTTP Server

A webpage served with the standard library's HttpServer. The server owns the accept loop and all HTTP framing (request parsing, status line, Content-Length); the handler lambda only inspects HttpRequest and fills in HttpResponse.

Build:

./build.sh

Run and open http://127.0.0.1:8080 (opens in a new tab) in a browser:

./server

Or verify from another terminal:

curl -i http://127.0.0.1:8080/         # 200 with the page
curl -i http://127.0.0.1:8080/missing  # 404

Source

server.sun
// HTTP server: serves a small webpage on http://127.0.0.1:8080
using sun;
 
function main() i32 {
  var alloc = make_heap_allocator();
  var server = HttpServer(alloc);
  try {
    server.bind_loopback(8080);
  } catch (e: IError) {
    println("Failed to bind 127.0.0.1:8080 (port in use?)");
    return 1;
  }
  println("Serving on http://127.0.0.1:8080");
 
  server.serve(
    lambda (req: ref HttpRequest, resp: ref HttpResponse) void {
      if (req.path.equals_literal("/")) {
        resp.set_body("<html><body><h1>Hello from Sun!</h1><p>This page is served by the Sun standard library.</p></body></html>");
      } else {
        resp.set_status(404);
        resp.set_body("<html><body><h1>404 Not Found</h1></body></html>");
      }
    }
  );
  return 0;
}
 
manifest {
    moons: ["stdlib.moon"]
}