A LANGUAGE FOR PRODUCTIVITY

This file is a "living" document of notes and ideas for a productivity-focused programming language. I do not mean to imply languages of the modern age are designed with productivity as an afterthought. In fact, it's quite the opposite. Languages like Python, JavaScript, C++, and Rust were all designed to be more "expressive" and claim to improve productivity.

However, these languages have a common pitfall: productivity is relative to the amount of philosophical buy-in.

A programmer only becomes more productive in these languages as they do things the "right way" (as defined by the language or expected domain). The moment they do things outside of the philosophy they have to fight the language or change their solution to fit back in the box. If they're lucky, the language designers will evolve the language to fit the domain at the cost of complexity.

See:

. . .

THE COMPILER SHOULD BE EASY TO IMPLEMENT

THE LANGUAGE SHOULD STAY SIMPLE

CUSTOM MEMORY MANAGEMENT

Most designers pick a memory model and throw away the others, limiting the number of applicable domains.

KEYWORDS OVER DIRECTIVES

USER-LEVEL CODE OVER INTRINSICS

RESTRICTIVE SYNTAX

FOLDER-BASED PACKAGES & EXPLICIT DEPENDENCIES

NO MORE VOID POINTERS

NO MORE IMPLICIT CONVERSION

TYPES SHOULD DESCRIBE THE DATA

DATA SHOULD BE MUTATED

ERRORS SHOULD JUST BE VALUES

THE BUILD SYSTEM SHOULD BE THE LANGUAGE

COMPILER TOOLING SHOULD NOT BE FIRST CLASS

. . .

SYNTAX IDEAS

Some syntactic ideas to outline how certain features would work:

Structs and methods:

// struct declaration
User :: struct {
	Name    string    // string type (pointer + length)
	Age     sint      // signed integer
	Friends [..]*User // dynamic array type
}

// "constructor"
MakeUser :: fn(name string, age sint) User {
	// type-inferred struct literal
	return .{
		Name:    strings.Clone(name),
		Age:     age,
		Friends: .[], // type-inferred array literal
	}
}

// Method (first argument is User)
AddFriend :: fn(u *User, f *const User) void {
	if u.Friends == nil {
		u.Friends = mem.Make([]*User, 1)
	}
	
	// error handling
	mem.Append(*u.Friends, f) catch e {
		log.Fatal(e)
	}
}

RemoveFriend :: fn(u *User, f *const User) void {
	if u.Friends == nil {
		return
	}
	
	// for-each loop (i is optional)
	for fp, i in u.Friends {
		if fp != f {
			continue
		}
		
		// ...
	}
}

RemoveFriendById :: fn(u *User, id usize) void {
	if u.Friends == nil || id >= len(u.Friends) {
		return
	}
	
	// ...
}

// Return type is always required
Release :: fn(u *User) void {
	if u.Name != nil {
		mem.Release(u.Name)
	}
	
	if u.Friends != nil {
		mem.Release(u.Friends)
	}
}

main :: fn() void {
	// variable declarations
	bob := MakeUser("Bob", 88)
	jon := MakeUser("Jon", 94)
	
	// scoped defer
	defer {
		bob.Release()
		jon.Release()
	}
	
	// method calls
	bob.AddFriend(*jon) // pointers
	jon.AddFriend(*bob)
	
	bob.RemoveFriend(jon)
	bob.RemoveFriendById(id: 0) // named arguments
}

Build system:

A small example of building the language in the language.

// within a build.lang file
build :: fn(package *build.Package, options *build.Options) !void {
	package.* = .{
		Type:    .Binary,
		Name:    "my package",
		Version: "0.0.1",
		Authors: .[ "my name <my@name.email>" ],
		Link:    "https://github.com/my-name/my-package",
	}
	
	// change build based on command-line arguments passed to the build command
	args := options.Args
	for arg in args {
		// switch statement (each branch breaks unless continue is used)
		if arg in {
			case "-d", "debug", "--debug":
				options.DebugInfo    = true
				options.Optimization = .None // type-inferred enum literals
			
			case "-r", "release", "--release":
				options.Optimization = .ReleaseSmall
				
			case:
				options.DebugInfo    = true
				options.Optimization = .None
		}
	}
	
	// anonymous switch statement
	// (breaks on the first true branch unless continue is used)
	if {
		case options.Optimization == .None:
			fmt.Println("Compiling with no optimizations...")
			continue
			
		case options.DebugInfo:
			fmt.Println("Compiling with debug info...")
			continue
			
		// a default case here would always be executed
		// case:
	}
}