Skip to the content.

A Word of Warning

“Perfect is the enemy of good.”

Slug will never end,
but still I write and mend.
Perfect waits and fades —
good enough, again.

Slug is a work in progress, and may never reach a stable release. However I am using it for all of my personal projects in an effort to keep it alive and useful.

Contents

Installing Slug

Download a Precompiled Binary from GitHub

You can download the latest release from the Slug releases page. Grab the latest release for your platform and architecture and extract the binary.

Once you the Slug release you will need to configure your PATH to include the slug binary, see local setup for more details.

IMPORTANT: If you are running OSX you will also need to fix permissions on the binary:

xattr -d com.apple.quarantine ./bin/slug
codesign -s - --deep --force ./bin/slug

What do these commands do?

Build from source

If you have Go installed, you can build from source:

git clone https://github.com/babyman/slug-lang.git
cd slug-lang
make build

Local setup

Once you have a slug binary, you will need to export $SLUG_HOME and add the binary to your $PATH. $SLUG_HOME is the directory where slug will find its libraries.

# slug home
export SLUG_HOME=[[path to slug home directory]]
export PATH="$SLUG_HOME/bin:$PATH"

Sublime Syntax Highlighting

If you are using Sublime Text, you can install the Slug Syntax Highlighting by downloading the package and placing it in your Sublime Text Packages/User directory.

Docker

Running slug in a container is also possible, I’m using podman but the same commands will work with docker:

# build the image
podman build -t slug .
# running an example script
podman run -v $(pwd):/data slug ./extras/examples/password-generator.slug

See extras/scripts/slug-cli.sh for a simple wrapper script to run the docker image.

Slug Command

Shell scripts

The following shell script structure should also work with slug on your system path

#!/usr/bin/env slug
println("Hello Slug!")

CLI

slug --root [path to module root] script[.slug] [args...]

Repl

Stay tuned for this feature.


Introduction to the Slug Programming Language: A Developer’s Guide

Welcome to the Slug Programming Language! Slug is a versatile, functional-first language that blends simplicity, expressiveness, and power to enable developers to write readable and maintainable code. This tutorial will introduce you to the core concepts of Slug, its syntax, and its idiomatic use cases.

By the end of this guide, you will have a foundational understanding of Slug’s features, allowing you to build structured, functional, and elegant programs.


1. Getting Started with Slug

Writing Your First Slug Program

Create a file called hello_world.slug and add the following:

var {*} = import("slug.std");

println("Hello, Slug!");

Run it with:

slug hello_world.slug

You should see:

Hello, Slug!

2. Core Building Blocks

Keywords in Slug

Below is a concise reference list of the keywords in Slug, along with their descriptions. These keywords form the foundational building blocks of the language.

Types

Comments

Variable Declarations

Control Flow

Functionality

Error Handling


Built-in Functions

Slug provides a small set of built-in functions designed to serve core operations and promote simplicity in program design. These built-ins are globally available and do not require explicit imports. Here’s an overview of the built-ins:

import

val {*} = import("slug.std");

len

val size = len([1, 2, 3]);       // 3
val textLength = len("hello");   // 5
  print("Hello", "Slug!");       // Outputs: Hello Slug!
  println("Welcome to Slug!");   // Outputs: Welcome to Slug!\n

Variables in Slug

Slug supports two types of variable declarations:

  1. Mutable Variables (var): Can change over time.
  2. Immutable Variables (val): Fixed once initialized.

Examples:

var {*} = import("slug.std");

var counter = 0;  // Mutable variable
val greeting = "Hello"; // Immutable constant

counter = counter + 1;      // Reassigning is allowed with var
counter /> println();       // Prints: 1

Functions and Closures

Functions are first-class citizens in Slug. They can be passed as arguments, returned from other functions, or stored in variables.

Example of defining and calling functions:

var {*} = import("slug.std");

val add = fn(a, b) { a + b }  // A function that adds two numbers
add(3, 4) /> println();       // Output: 7

Functions can close over their surrounding environment, making them closures:

var {*} = import("slug.std");

val multiplier = fn(factor) {
    fn(num) { num * factor }
};

val double = multiplier(2);
double(5) /> println();  // Output: 10

Function Chaining in Slug

Slug supports function chaining, which allows for a cleaner and more expressive syntax. When a variable is placed before a function call in the format var /> call(), it is automatically passed as the first parameter to the function. The result is equivalent to invoking the function as call(var).

Example:

slug var {*} = import("slug.std");

1 /> println(); // Using function chaining Outputs: 1
println(1);     // Equivalent traditional function call

By supporting function chaining, Slug simplifies code readability and enables a more fluid programming style, especially when writing pipelines or working with multiple transformations.

Function Call Dispatch and Type Hints

Slug’s function call dispatch mechanism determines the correct function to call in cases where multiple function signatures might match incoming arguments. The dispatch process evaluates function signatures based on the provided arguments and their compatibility with type hints.

Function Dispatch

If no suitable match is found, an error is returned mentioning that no valid function exists for the given arguments.

Using Type Hints

Type hints, represented by tags in Slug, help guide the dispatch process by associating function parameters or objects with specific types.

Supported Type Tags:

Example: Function with Type Hints

Suppose we define two functions where each variation operates on different types:

fn add(@num a, @num b) { a + b }
fn add(@str a, @str b) { a + b }

Tips for Writing Functions with Hints

  1. Use type tags (@num, @str, etc.) to clarify expected parameter types.
  2. Define fallback or general-purpose functions to handle unexpected cases.

This function concatenates any number of strings passed as arguments.


3. Functional Programming Constructs

Slug excels at functional programming, empowering you with concise tools and expressive patterns.

Map, Filter, and Reduce

var {*} = import("slug.std");

val list = [1, 2, 3, 4, 5];

val squares = list /> map(fn(v) { v * v });           // [1, 4, 9, 16, 25]
val evens = list /> filter(fn(v) { v % 2 == 0 });     // [2, 4]
val sum = list /> reduce(0, fn(acc, v) { acc + v });  // 15

squares /> println();
evens /> println();
sum /> println();

Pattern Matching

Use match to destructure and inspect values directly.

var {*} = import("slug.std");

val classify = fn(value) {
    match value {
        0 => "zero";
        1 => "one";
        _ => "other";  // Catch-all case
    }
};

classify(1) /> println();  // Output: one
classify(5) /> println();  // Output: other

match can also destructure complex data like lists:

var {*} = import("slug.std");

val sumList = fn(list) {
    match list {
        [h, ...t] => h + sumList(t);  // Head and Tail destructuring
        [] => 0;                      // Base case
    }
};

sumList([1, 2, 3]) /> println();  // Output: 6

Higher-Order Functions

Slug supports higher-order functions: functions that accept and return functions.

Example:

var {*} = import("slug.std");

val applyTwice = fn(f, v) { f(f(v)) };

val increment = fn(x) { x + 1 };
applyTwice(increment, 10) /> println();  // Output: 12

4. Data Structures

Lists

A list is a collection of elements. It supports operations like indexing, appending, and slicing.

var {*} = import("slug.std");

val list = [10, 20, 30];
list[1] /> println();    // Output: 20
list[-1] /> println();   // Output: 30
list[:1] /> println();   // Output: [10]
list[1:] /> println();   // Output: [20, 30]

Maps

Maps are key-value stores in Slug.

var {*} = import("slug.std");

var myMap = {};
myMap = put(myMap, "name", "Slug");
get(myMap, "name") /> println();  // Output: Slug

5. Flow Control

Conditionals: if/else

var {*} = import("slug.std");

val max = fn(a, b) {
    if (a > b) {
        a
    } else {
        b
    }
};

max(3, 5) /> println();  // Output: 5

Error Handling with try/catch and throw

var {*} = import("slug.std");

val process = fn(value) {
    try {
        if (value < 0) {
            throw NegativeValueError({msg:"Negative value not allowed"});
        }
        value * 2
    } catch (err) {
        {...} => println("Caught error:", err.msg);
        _ => nil;
    }
};

process(-1) /> println();  // Output: Caught error: Negative value not allowed

6. Working Example: Functional Data Pipeline

We’ll build a pipeline that processes a list of numbers by:

var {*} = import("slug.std");

val numbers = [1, 2, 3, 4, 5, 6];

val result = numbers
    /> map(fn(x) { x * x })          // [1, 4, 9, 16, 25, 36]
    /> filter(fn(x) { x % 2 == 0 })  // [4, 16, 36]
    /> reduce(0, fn(acc, x) { acc + x });  // 56

println("Result:", result);  // Output: Result: 56

7. Writing and Running Tests in Slug

Slug provides an integrated testing mechanism with the use of the tags @test and @testWith. These tags simplify the process of writing unit tests for your Slug code and enable test-driven development by allowing you to define and execute tests directly within your modules.

Using @testWith

The @testWith tag is used for parameterized tests, where a single function can be tested with multiple sets of inputs and expected outputs. This allows for concise and comprehensive test coverage.

To create a test with @testWith:

@testWith(
    [3, 5], 8,
    [10, -5], 5,
    [0, 0], 0
)
var parameterizedTest = fn(a, b) {
 a + b; 
}

Using @test

The @test tag marks a function as a test case. These functions are executed independently, and the results of assertions or errors during their execution determine if the test passes or fails.

To create a simple test using @test:

var {*} = import("slug.test");
@test
var simpleTest = fn() {
    val result = 1 + 1;
    result /> assertEqual(2);
}

Running Tests

Slug automatically detects and runs all test functions (@test and @testWith) in the given module. You can run tests for one or more modules by specifying their paths when invoking the test runner:

slug test path_to_source.slug

Example Output:

Results:

Tests run: 33, Failures: 0, Errors: 0

Total time 1ms

With @test and @testWith, Slug empowers you to write robust, maintainable tests that enhance code quality and reliability.

8. Reference

Operator Precedence and Associativity

Prec Operator Description Associates
1 () [] . Grouping, Subscript, Method call Left
2 - ! ~ Negate, Not, Complement Right
3 * / % Multiply, Divide, Modulo Left
4 + - Add, Subtract Left
6 « » Left shift, Right shift Left
7 & Bitwise and Left
8 ^ Bitwise xor Left
9 | Bitwise or Left
10 < <= > >= Comparison Left
12 == != Equals, Not equal Left
13 && Logical and Left
14 || Logical or Left
15 ?: Conditional* Right
16 = Assignment Right