Try Wake Now


WAKE

a fast, expressive, typesafe language that gives you testability from the ground up.

A Modern Programming Language

Wake is a brand new programming language aimed at solving modern problems, such as code testability, expressive typesafety, and language interoperability.

Throughout the docs, we'll show you how we've made the language testable, flexible, readable, concise, and safe.


Quick Code Introduction

We'll compare Wake to both java and javascript. You'll see friendly classes, clean code, and intuitive type safety.

or you can jump straight in:

Wake

Code as concise as javascript, with inheritance and typesafety and more
Wake
every FibGenerator is:
    needs Printer;

    Num -- generate( Num ) {
        if(Num == 0) return 0;
        if(Num == 1) return 1;
        return generate(Num - 1) + generate(Num - 2);
    }

    Num -- sumFrom( Num )To( $Num ) {
        var Num sum = generate(Num);
        if(Num > $Num)
            return sum + sumFrom(Num - 1)To($Num);
        else return sum;
    }

    printUpTo( Num ) {
        if(Num > 0) printUpTo(Num);
        Printer.print(generate(Num));
    }

Javascript / Java

Elegant and concise, but unintiuitive, unsafe, with pitfalls all around
Javascript
var FibGenerator = function(console) {

    this.generate = function(x) {
    	if(x == 0) return 0;
        if(x == 1) return 1;
        return this.generate(x - 1) + this.generate(x - 2);
    }

    this.sumFromRange = function(top, bot) {
        var sum = this.generate(top);
        if(top > bot)
            return sum + this.sumFromRange(top - 1, bot);
        else return sum;
    }

    this.printUpTo = function(x) {
        if(x > 0) this.printUpTo(x);
    	console.log(this.generate(x));
    }
}

New ideas in Wake

We've both created and borrowed some cutting edge ideas.

Provisions

testable flexible

Use provisions to keep a testable and extensible seam between every class.

This makes testability and flexibility a language feature.

Type Inference

concise

Keep the expressive, clean look of dynamic languages, with a safe type system.

This saves you time writing, reading, and refactoring code.

Constructor Properties

testable concise

Reduce boiler-plate code by making constructor arguments exactly like properties.

This makes best practices like Aspect-Oriented-Programming intuitive.

Two-way polymorphism

testable flexible

Eventually will add this.

This creates testable inheritance by increasing expressive object design.

Compilation targets as a language feature

flexible

A goal of Wake is to compile into anything -- .net, java, javascript, and assembly

This makes Wake libraries usable in every one of your projects.

Closures

flexible concise

Wake is part of a small class of strongly-typed, object oriented languages with functional coloring.

This adds concise, elegant, rapid-development to your scalable enterprise apps.

What Wake did without

Any poor feature in a language can prevent the addition of a powerful one. We made some hard choices to create a strong cocktail of ideas.

What we cut Why we cut it What it enabled
Static methods
Using a static method from a procedure hard-codes implementation rather than interface, decreasing flexibility and testability
FIXED You can (and usually should) now use exact type names as variable names, meaning more clarity and fewer keystrokes.
The new keyword
new mixes application APIs with implementations, and has been replaced with 'provisions.'
FIXED Constructors directly translate to properties since they are not functions.
Interface/extension distinction
A common best practice is to create an interface for every class, even before its used, resulting in junk code and extra boilerplate.
FIXED In Wake you can use any class as an interface, making everything driven by APIs without any additional code.
Abstract classes
Abstract classes are a combination of an interface and a class, or a means of locking a class to static methods.
FIXED We let the compiler tell you if a class is incomplete, not vice versa, without any management on your part.
Primitive types
Primitive types are fast, but without methods they require external behavior to manipulate them.
FIXED The compiler handles autoboxing primitives into objects - Num, Bool, Text, etc - and lets you code without managing the conversion.
Cryptic keywords
Abstract, virtual, volatile, and other cryptic keywords from the 1970s convey very little about what they actually do.
FIXED New programmers will find every keyword to be simple, and meaningful, from Text to capable to needs.

Try Wake Online

We handled installing it for you, and with javascript as a compile target, you can run it all right in your browser

Try Wake Online


Making The Most Of Wake Variables

Variables in Wake come in three forms, all with the goal of meaningful names in minimal keystrokes.

1. Typenames as variables concise readable

Since we got rid of static methods, we can save you from repeating yourself meaninglessly in domain-driven code.

Wake
every Person is:

    needs Num;

    Num -- getId() {
        return Num;
    }

    Bool -- isSameAs(Person) {
        return getId() == Person.getId();
    }
Java
class Person {

    private int id;

    public Person(int myid) {
        id = myid;
    }

    public int getId() {
        return id;
    }

    public bool isSameAs(Person person) {
        return getId() == person.getId();
    }
}

2. Aliasing readable safe

In our previous example, calling a person's ID Num is a bit ambiguous. When a type isn't meaningful for a context, use aliases.

To aid the compiler, we must write aliases in lowercase, to hint that its not a type. But luckily, it turns out that the same idea is useful to humans as well. It also allows us to write aliases before or after our type to make our code more literate.

Wake
every Person is:

    needs Num id;

    Num -- getId() {
        return id
    }

    setIdThenSave(newid Num, Bool recursively) {
        ...
    }
Java
class Person {

    private int id;

    public Person(int myid) {
        id = myid;
    }

    public int getId() {
        return id;
    }

    public void setIdThenSave(int id, bool recursively) {
        //...
    }
}

3. Shadowing concise safe

Following from the idea that the most meaningful name for a variable often is its type, we have a problem with places like 'setter' methods. The new value is of the same type as the old value, but they must be distinguished.

Instead of creating meaningless aliases as we arguably did with newid, we can add $ to preserve type information while also creating a unique instance

Wake
every NumContainer is:

    needs Num;

    Num -- setToNotGreaterThan($Num) {
        if(Num > $Num) Num = $Num;
    }

Declaring variables concise safe

All these variable names are usable in declarations when preceded with :, or when in properties

Wake
every DeclarationExample is:

    with Num here = 4;
    with some Num = 4;
    with $Num = 4;
    with public Num too = 5;

    needs another Num, $$Num, Num again;

    declareThem() {
        var local Num = 4;
        var second Num = 4;
        var $$$Num = 5;
        var Num = 3;
    }

Optional Types typesafe

Wake will guarantee the existence of most variables, arguments, and return types. This prevents rogue NullPointerExceptions, or segfaults, or fatal errors that large apps in other languages frequently succumb to.

The flip side to this is that when a value may not exist, it must be declared optional with ?. Before using an optional type, the compiler ensures you have accounted for the possibility of it not existing with exists clauses.

Wake
every OptionalTypeUsage is:

    Num? -- mayReturnAnNumAndAcceptsAn(Num?) {
        Num = 5;
        Num = Nothing;
        if Num exists {
            return Num + 7;
        } else {
            return Nothing;
        }
    }

Using Lists typesafe

For ease of use, the type returned by T[x] is a T, and not a T?. For this reason, getting an unassigned list index will throw an exception so that the type system is happy. Safe array accesses which return T? and do not throw are on the way.

Lists (called Arrays in many other languages) are a way of storing sets of a particular type. They are incredibly useful.

Wake
every ListUsage is:

    methodWithListArgument(Num[]) {
        Num[0] = 5;
        var $Num[] = [];
        var $$Num[] = [1, 2, 3];
        var combined Num[][] = [Num[], $Num[], $$Num[]];
    }
You will get an error if you try to declare Num[] in the same scope as Num[][], and for good reason.

Unless lists of lists in wake to look like Num[][0] or Num[0][], we must face an ambiguity problem when Num[] and Num[][] share a scope. Since Num[0] is the correct syntax for both of these variables, the compiler forces you to rename one or the other. This is not the case for Num[] and Num.


Iterating over Lists concise readable typesafe

Since Wake blurs the line between variables and types, we can boast the smallest foreach loop of any language.

Wake
every PersonDatabase is:

    delete(Person[]) {
        foreach(Person[]) Person.delete();
    }
This works as you would expect for shadowed variables, where $Person[] becomes $Person. And it also works for expressions, where foreach(db.getPeople()) lets you iterate over Person, since the compiler knows your db returns Person[].

Wake also plans to supports giving custom names to the items you iterate over, and will soon support getting the index at the same time.

Wake
every PersonDatabase is:

    delete(Person[]) {
        foreach($Person in Person[]) $Person.delete();
        foreach(person in Person[]) person.delete();
        foreach(Person[] at Num) Person.greet(Person[Num]);
        foreach(person in Person[] at Num) person.greet(Person[Num]);
    }

Classes in Wake concise readable

Wake is a truly Object Oriented language, and should come with few surprises here.

In Wake, everything is an object. A class declaration begins with the keyword every as seen below. Within that definition can be properties, dependencies, a constructor, methods, and provisions.

Wake
every MyClass is:
    with public Text name;
    with Text ssn;

    needs Num id, Printer then {
        // constructor body
    }

    provides Text:URL <- 'http://www.wakelang.com';

    Text -- getSSNLastFour() {
        // ...
    }

Dependencies concise testable

When an object is constructed, first the dependencies are all resolved by means of provisions. This means that you can simply say your object needs Printer, and you will always have one.

Wake
every FileStream is:
    needs Text filename, public OutStream;

Properties flexible

At this point the object has its dependencies met, and each property can use the needs in their initialization. Then the constructor is called.

Wake
every IncrementingProperties is:
    needs Num starter, Printer {
        Printer.print(sum);
    }

    with Num plusone = starter + 1;
    with Num plustwo = starter + 2;

Inheritance testable flexible readable

Every class can be inherited with or without keeping behavior, with the keywords a/an, and capable. Usually in small projects the point of inheritance is to copy the code from that class, however, it is not always desirable. If you use capable, you must rewrite the methods of your child class. These actions are called extending vs implementing.

An additional value to implementing classes with capable instead of extending classes with a/an, is that you can implement multiple classes even though you can only extend one class.

An object which extends or implements another class can be used as that subclass elsewhere in the code.

Wake
every BaseClass is:

    myMethod(Printer) {
        Printer.print("BaseClass");
    }

every SubClass (a BaseClass, capable Printer) is:

    needs Printer;

    print(Text) {
       Printer.print("SubClass printing: " + Text);
    }

    useThisAsPrinter() {
        myMethod(this);
    }

Valid Methods in Wake concise readable

Methods in Wake can look strange at first. The changes are simple and make your APIs read like sentences.

In java, a detailed method signature may look like:

addExaminerToContractWithMedicalCompany(Examiner examiner, MedicalCompany company)

We decided we can do better. In Wake you could rewrite that method as:

add(Examiner)ToContractWith(MedicalCompany)

Here are some examples:

Wake
every MethodExample is:
    ReturnType -- methodWithReturn() {}
    methodWithNoReturnValue() {}
    abstractMethodWithNoImplementation();
    methodWithArgument(Num) {}
    methodWithArgument(this Num) {}
    methodWithArgument(Num here) {}
    methodWith(Num)ArgumentAnd(Text)Argument() {}

Invoking Methods readable

Invoking methods (that is, calling a method's code on a particular object and argument set) looks almost exactly like defining one.

Wake
every MethodInvoker is:

    methodWithArgument(Bool) {
        methodWithArgument(!Bool);
    }

Method Overloading readable concise

Method overloading can create more confusion than it prevents. As a general rule, avoid using overloading and aliases together for the clearest code.

Method overloading exists in C++ and java. A method can accept differently typed objects, and have different behaviors for each. Used properly, it creates shorter method names that focus on behavior rather than implementation.

Wake
every CrossTypeComparator is:
    Bool -- compare( Text )And( $Text ) {
        //...
    }

    Bool -- compare( Num )And( $Num ) {
        return Num < $Num;
    }

Generics flexible typesafe

Like Java, C#, and other languages, wake supports generic types.

Generics are required for a powerful static type system. Unlike wake, Java and C# did not have generics in early versions. As such, the type safety and performance of arrays in these languages was thrown out to accomodate missing flexibility. Wake had generics from version one and does not suffer these drawbacks.
The wake syntax uses curly braces instead of angle brackets for generics. This simple difference speeds up compilation and makes parsing easier for independent programs, as it is a 100% context-free grammar.
These examples use function types that are not yet implemented.
Wake
every CalculationCache{T} is:
    with T? = nothing;
    needs T -- fn() calculation;
    T -- getValue() {
        if(T == nothing) {
            T = calculation();
        }
        return T;
    }

The type information of a generic is deffered until the moment where its used. For those unfamiliar with generics, using one would look like this.

Wake
every Squarer is:
    needs Num;
    provides CalculationCache{Num};
    with CalculationCache{Num} <(Num -- fn() { return Num * Num; }) this;
    Num -- get() {
        return CalculationCache.getValue();
    }

Provisions flexible testable typesafe

Testability was a huge goal of Wake's design. We were inspired by dependency injection frameworks to make a faster, typesafe means of binding dependencies to behavior. What we came up with is provisions.

What in other languages may be a static method, or a call to new another object, or a helper function call, instead is fetched from a provider. Here we look at how.

Wake
every UsesProvider is:

    needs Provider, DependentClass:Special then {
        var Printer from Provider;
        (Printer from Provider).print(Text);
    }

Defining Plain Provisions concise typesafe

A plain provision simply marks that your class can provide a type. The compiler will ensure that you can provide all of that type's dependencies as well.

Wake
every Provider is:

    provides Printer, OtherClass;

Providing Subtypes flexible testable typesafe

The point of provisions is to make all dependencies or created objects be replacable with subtypes, at the creator's discretion. This is called Inversion Of Control. You can provide mock objects for testing, easily swap logging methods, and more.

Wake
every Provider is:

    provides Printer <- DisabledPrinter;

Specialized Provisions typesafe flexible

Sometimes a classname is not detailed enough to know your dependencies will be properly provided. Using what we call Specialties, you can provide and require specific objects.

Wake
every DBProvider is:

    provides
        User,
        DBConnection:Default;

every User is:

    needs DBConnection:Default;

Primitive Provisions typesafe

Primitive provisions cannot be plain, since the compiler can't tell you what Num to provide. Additionally, they must be specified, to ensure you get the right value. These are the equivalent of constants in Wake, and bind directly to a primitive value.

Wake
every PrimitiveProvider is:

    provides Text:Username <- "MyUser", Num:Port <- 3306;

Constructor Provisions flexible

We call this constructor provision since it most closely resembles constructors in languages like Java. However, it is merely a way of matching provisions to specific dependencies.

Wake
every Provider is:
    provides
        Num:Port <- 3307,
        Text:Username <- "Test",
        DBConnection:Test <- DBConnection(Text:Username, Num:Port),
        User <- TestUser(DBConnection:Test);

If a class with needs extends another class with needs, constructor provisions require that you supply all of these. When doing this, supply the childclass needs first, and then the parent class needs, in that order. And don't worry, the compiler will make sure you don't mess up.

Provision Arguments flexible typesafe testable

Not all object needs should be satisfied by the dependency injection in wake. We stil want to be able to pass in values when we call a provision. Providers can be coded up to do this.

Wake
every Person in:
    needs Text firstname, Text lastname;

every PersonProvider is:
    provides Person <- Person(?Text, ?Text);

    usePersonProvision() {
        var Person('Bobby', 'Ratsnest') from this;
        var second Person("Bobby", "Ratsnest") from this;
        var Person three("Bobby", "Ratsnest") from this;
    }
The syntax ?Text has two purposes. Firstly, it documents the need types on the API where you will look for them: the provider you are using. Secondly, it allows you to ask for a more specific type than the object you're providing actually requires. i.e., when providing an object which only needs a Printer, you could ask for a CachingPrinter instead.

For classes that inherit needs, follow the instructions in the Constructor Provisions section on which order to specify them.

Behavioral Provisions flexible

Provisions can be treated as a code body with a return type where necessary. Note that the end result must be a provision; however it can be used to configure objects or provide different objects conditionally, or create a singleton.

Wake
every Provider is:

    needs Session then {
        Session.loadFile(Text:SessionFile < this);
    }

    provides
        Text:SessionFile,
        Session <- { return Session; };

You can also have arguments to your behavioral provisions, so that you can mock out provisions with arguments, and/or perform your own unique operations required.

every Provider is:
    needs BabyMaker;
    provides Child <- (Person dad, Person mom) {
        return BabyMaker.makeBabyWithFather(dad)AndMother(mom);
    };

Ternary Operators readable concise

Wake uses python style ternary operators, which are more readable than the c-style :? approach.

Wake
every MyClass

    Num -- myMethod(Bool) {
        return 4 if Bool else 5;
    };

Annotations readable testable flexible

Wake now has unchecked annotations. We have a plan to typecheck the values you use, in which annotations, on what parts of your source code. But for now, you can freely annotate classes, methods, and needs with values such as Texts, Bools, Nums, and even nothing. This is currently used by our unit test library, but can be used for so so much more.

Wake

@TestClass
@MadeUpAnnotation("hello", false, 123, nothing)
every MyClass

    needs
        @MadeUpAnnotation("hello", false, 123, nothing)
        Printer;

    @Test
    @MadeUpAnnotation("hello", false, 123, nothing)
    testSomething(Asserts) {
        // ...
    };

Program Entry Points flexible testable fast

After compiling each Wake file, you can link the files together and choose where the program begins. This means the code you write does not guarantee any particular entry point, which in turn means your test suite can have a different entry point than your app. Without this, you would be unable to test your application's startup in unit tests.

Any class with automatically inferrable dependencies (ie, no primitives, lists, etc) can be your startup class, and any of its methods which take no arguments can be the starting method. These are provided by the compile time flags -c and -m respectively, but default to Main and main().

This means that when you link your wake files together, the compiler generates a startup routine that wires up the complete object graph required by your main class, and invokes your desired startup method. This makes for a fast, flexible, and testable program startup.