WAKE

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

Wake Tutorial

Installing

first you need to install wake, or you can just read along and install it later.

A first program

Begin by cloning the git project seed with the command git clone http://github.com/MichaelRFairhurst/wake-project-seed.git. It will create a project with the following directory structure:

~/projects
  └──YourFirstProject/
     ├──bin/
     │  ├──obj/
     │  │  └──{compiled files here}
     │  └──tables/
     │     └──{stlib tables and your tables here}
     ├──lib/
     │  ├──obj/
     │  │  └──{3rd party library objects here}
     │  └──tables/
     │     └──{3rd party library tables here}
     ├──src/
     └──test/

All you need to do is run make from this directory to build and test your code. Test files go in the test directory and source files go in src. So lets start writing code!

Main.wk
import Printer;

every Main is:

	needs Printer;

    main() {
        Printer.printLine("Hello World!");
    }

This should all look quite familiar. We import the standard library's Printer for use in our code. We then create a class named Main, and that class will need a Printer. This is both a type name and a variable name, and a private property that will exist on every instance of the Main class.

The code every Main is: may look strange, but its simply a class definition. In addition to requiring a Printer, we give it one method, main() which returns nothing, and simply tells our Printer to print "Hello".

This is the most basic program in Wake, and it already exemplifies its usage of dependency injection.

You can run make to compile this file, and then node bin/myprogram to run it.

But this is just the easy stuff. What if we wanted to actually inject something ouselves?

Custom injections

Lets create a new class which holds the dependency on the Printer, and can greet more than just the world. Lets call this class, Hello.

Hello.wk

import Printer;

every Hello is:

    needs Printer, Text:HelloText;

    hello()
        Printer.printLine("Hello " + Text + "!");
    }

Once again we import Printer, and once again we have a class definition - every Hello is:. However, this time we need more than just a Printer, we also need a Text:HelloText. This is an injected Text identified by a custom name. We could have called it Text:WhatAGreatTutorial instead if we'd wanted to.

The important part of our HelloText is that its a Text. This tells the compiler that we have a private property named Text and of type Text, and allows us to use it in our method hello().

We then use java-style string concatenation to greet whatever our HelloText is.


Now we have some rewriting to do in Main.

Main.wk
import Printer;
import Hello;

every Main is:

    provides Hello,
        Printer,
        Text:HelloText <- "dependency injection!";

    main() {
        var Hello from this;
        Hello.hello();
    }

If we had said needs Hello instead of provides Hello, we wouldn't have been able to inject it with our custom Text:HelloText. Instead we must be able to provide it.

The other stuff we provide here is stuff needed by the class Hello. If we forgot to include it, the compiler would tell us to. We're going to provide a plain old Printer, but we specify exactly what we want for Text:HelloText.

In main() we say var Hello from this;, which creates a Hello using our Main object, and assigns it to a variable named Hello. Then we can say Hello.hello() to call methods on it.

Using Provision Arguments

Rather than greet just one thing, lets greet several. Obviously, we're going to run into some maintainability issues if we can't create these Hello's on the fly, so lets handle both looping and provion arguments at the same time.

Main.wk
import Printer;
import Hello;

every Main is:

    provides Hello <- Hello(Printer, ?Text),
		Printer;

    main() {
		var Text[] = ["World", "Wakers", "Seahawks", "Aliens"];
		foreach(Text[]) {
			var Hello(Text) from this;
			Hello.hello();
		}
    }

We changed our code in povides Hello to a specific class with a specific injection. We first give it a plain old Printer from ourselves, and then we give it ?Text, which means our provision for Hello has a single Text argument. We still have to be able to provide Printer.

Next we create a list of Texts containing some groups of people we'd like to say hello to, which will simply be named Text[].

The foreach is special. It knows that we have Text[] inside it, so it can automatically create a variable named Text for us inside. Its like magic!

Finally, we actually call our provision with our argument by saying Hello(Text) from this, once again using the var syntactic sugar to save it as a variable named Hello.

What Next?

The tutorial will leave you here. There is a plethora of docummented features, as well as a small standard library. You can take this knowledge to write libraries and share them, maybe even with us for integration with the standard libaries. Any questions, you can ask me via email at michaerfairhurst@gmail.com. There are also some lingering issues that have been documented here for if you run into anything strange.

Known Issues

These issues have the potential to crash/corrupt a running program. Some are pretty standard use cases - sorry! And others are pretty obscure/obviously wrong at compile-time.


Nested Optionals

Another thing I had not considered when implementing optionals is that "null" is an idequate runtime representation of a true optional type system. What can optional types do that nulls can't? Nest, that's what!

every Main is:

    nestedOptionalsInteractingWithExists() {
        var Num? = nothing;
        var Num?? nested = Num;
        if Num exists { } // this check will fail as it should
        if nested exists { } // this check should succeed, and won't!
    }

Basically, if you consider a language without optional types, if you had a function that searched through a list and returned either the last item or null, this API is inadequate for a list where the last element is null. The fix isn't too difficult, but optional types need to be stored in recursive wrappers, and unboxed when used within an "exists" check.


Generic Properties

When a generic class has a generic type served as a public property, it isn't paramterized the way methods are. The name doesn't change and the type doesn't change either.

wake
every Generic{T} is:

    with public T?;

    typeErrors(Generic{Num}) {
        var Num? = Generic.Num; // Doesn't compile: unknown property Num
        var Num? = Generic.T; // Doesn't compile: expected: Num?, actual: T?
        var T? wat = Generic.T; // Successfully compiles: WAT
    }

This is easy to fix, just low priority. Ping me for a, most likely, same-day fix.


Overlapping Generics

I knew about this issue I'm just a bit scared to really take it on in full. If you have two different types in a generic, you can declare & distinguish two methods by overloading. However, when you later use it with the two types matching, one method or another will quietly overwrite the other.

wake
every Generic{A, B} is:

    needs Printer;

    overlappingMethod(A) { Printer.printLine("MethodA"); }
    overlappingMethod(B) { Printer.printLine("MethodB"); }

    oneMethodIsLost(Generic{Num, Num}) {
        Generic.overlappingMethod(5); // prints B...I think...
    }

Missing Features + Workarounds

These missing functionalities should not crash or corrupt any programs, but they may get in the way of your designs. Sorry!


Parent Method Calls

This is a semantic analysis and parsing problem. I simply need to look for parent.aMethod() calls and treat them specially. Haven't done it yet. Also, I'm partially waiting on this one because I am pondering two-way-inheritance. There's an easy workaround, though!

wake
every ParentClass is:

    myMethod() {
        // this is inaccessible from Childclass.myMethod() !!
        // call parent_myMethod from here
	}
    parent_myMethod() { } // since this method IS accessible from both classes

every ChildClass is:

    myMethod() {
        parent.myMethod(); // Won't compile :(
        parent_myMethod(); // so create this method on ParentClass and have fun!
    }

Common Object Class

Its useful to have everything (and I mean everything) extend a single class, usually Object. A good example is database queries. It won't be hard to do, but its not done yet.

wake
every FetchUsersQuery is:

    needs PDO;

    getAllUsers(Object[] values) {
        return PDO.prepare("SELECT * FROM user WHERE username = ? AND email = ?", values).find();
    }

Exists on some L-Values

For properties on external objects, and potentially other places, you can't use exists because it's very complex to add the value to a scope sym table. You must store it in an intermediate variable, sorry.

wake
every Link is:

    with public Link? next;

    cannotCheckExistanceDownChain() {
        if next exists { // OK!
            if next.next exists { }// No bueno!!
            Link? farther = next.next; // OK!
            if farther exists {} // OK!
        }
    }

Namespaces

Yeah, just only have so much time. This one will be easy though.


"Nothing" as an Argument

This may be solved more generally eventually. Actually, its closer than ever now that generics have "casing" signatures that get filled in later. But basically, the compiler only finds methods by their signatures rather than their names. Since "nothing" is its own type, it never matches a method signaturee. Luckily you can cast it and achieve the same end.

wake
every MethodCallWithNothng is:

    getAllUsersWith(Text? firstame, Text? lastname) { ... }

    callGetAllUsersWithNothing() {
        getAllUsersWith("ted", nothing); // Won't compile! The compiler can't find the method
        getAllUsersWith("ted", (Text?) nothing); // Now the compiler can find the method :)
    }

Any big O and little O geeks out there should lend me hand on this one to create a fast binary tree or something. The only way that comes to mind for truly solving this problem would be O of n to the n, I think.


Generic Provisions

Not sure exactly how to put this one. Its a pretty big hole in wake. I'll list the things you can't do, because otherwise its tough to describe.

wake
every GenericProvider{T} is:

    provides T, // won't compile. How can we provide T when T could require any sort of subsequent types?!
         GenericProvider, // This should compile, since all types of GenericProvider have one type of need
         GenericProvider{Num} <- SomeSubclassOfGenericProvider; // It should be possible to do this too, not sure how best to make this happen.

    useAProvider() {
        T from SomeProvider; // Won't compile, for the same reason as java won't let you do "new T()". Likely will never fix this...its broken for very good reasons.
    }

Generic Inheritance

This just needs to happen, and its actually not so intimidating a feature. Also may hold up stdlib creation where generic inheritance is crazy.

wake
every Set{T} (a Collection{T}) is:

Generic Methods

This is painfully obvious in Asserts. However its quite a difficult item to do...it requires some sweet type inference.

wake
every Asserts is:

    {T extends Printable} that(T)Equals($T) {
        if(T != $T) ...
    }

Also syntax is ugly. What can ya do.


Generic Ranges

At the moment all generics operate on any and all types. That is to say, a Generic{T} can be created with absolutely any type. It should be able to set a lower and upper limit. Don't worry, I anticipated this. Shouldn't be too bad once I get around to it.

wake
every Generic{P from Printer, L to Printer} is:

    myMethod(P, L) {
        P.printLine("not possible until 'P from Printer' is allowed.");
        L = P; // Possible since the upper limit of L is a subclass of the lower limit of P :)
    }

Switch/Case

Actually, even though it currently parses switch/case like java, I want to revamp it. I have some good ideas but I'm a bit afraid of them, so they're on hold.


Abstract Provisions

I wasn't entirely sure how typing for providers was going to work. There's still room for a great idea to make providers feel more dynamic. However, in the meantime, this uncertainty clouded my eyes so I didn't think of abstract provisions.

Essentially, since all classes are interfaces, and pure interface classes simply make all their methods abstract, there needs to be a way to say a class "provides SomeClass" without actually providing anything.

wake
every MyInterface is:
    provides AClass; // Won't compile: need to provide AClass's dependencies.
    // Or, inheritingfrom MyInterface gives you behavior you didn't realize

    myAbstractMethod();
    anotherAbstractMethod();

Reflection

Actually shouldn't be too bad to implement. But its not done yet, and its a big undertaking.


Enums

Somebody who loves the feature-rich enums of java, and who understands Enum<E extends Enum<E>> should tell me all of the things I need do and not do to make powerful enums.


Recursive Imports

If two classes use each other, just stick them in one file and you're good. If you split them up into two different files you're screwed!