I've spent the morning tinkering with Wake because I had time and it popped up on Twitter at the right time.
It's an interesting language that aims to be
a fast, expressive, type safe language that gives you testability from the ground up.
What follows is a write up of my thoughts from the short time I've spent with Wake.
Baked in Dependency Injection
I have plenty of experience in Java and have seen my fair share of codebases that make extensive use of dependency injection. The verbosity of Java has created many a monster. Providers
, Factories
, FactoryProviders
and ProviderFactoryProviders
fill project hierarchies all in an attempt to make the language bend to our will.
The main problem, aside from the obvious verbosity of Java, is its lack of syntax to support dependency injection. Vast technologies have been built around this simple concept alone. However without the language level support you are forced to go for either wizardry (see @Inject
) or verbosity (through additional annotations) - neither of which are desirable.
One of Wakes fundamental features is the use of provisions which is essentially a means to bind dependencies to particular behaviour. Classes define exactly what they need
and provide
in order to get the job done.
import Printer;
every Main is:
needs Printer out;
main() {
out.printLine("Hello");
}
In the example above we can see that Main explicitly needs
Printer
. When the application is executed an instance of Printer
will be injected into our Main
. In this case Wake knows how to inject Printer
as it is from the standard library. We can also provide
our own dependencies including any rules required to construct them.
import Printer;
every Main is:
needs
Printer;
provides
Person <- Person(Printer, ?Text),
Printer;
main() {
(Person("James") from this).sayHi("Internet");
}
every Person is:
needs
Printer out,
Text me;
sayHi(Text otherPerson) {
out.printLine("Hello " + otherPerson + " I'm " + me + "!");
}
There is a bit more code here so let me break down some of the important lines.
- We have a
Person
class. This classneeds
both aPrinter
and someText
that represents the persons name. - The
Person
class has avoid
method that takes a singleText
argument and uses the injectedPrinter
to print a greeting. - The
Main
class, as before,needs
aPrinter
- This time the
Main
class alsoprovides
a few things. Itprovides
aPrinter
which I will expand on shortly and it alsoprovides
aPerson
type. More so it specifies a way to construct aPerson
instance without having to specify aPrinter
each time -Person(Printer, ?Text)
. In essence this is saying when Main is asked to provide and instance of Person always use this particularPrinter
instance and take aText
argument - Our
Main
class is called via themain()
entry point and at this point creates a newPerson
instance using thePerson
provider defined inMain
(hence the use ofthis
-(Person("James") from this)
) - We call
sayHi
on this provided instance.
It may seem initially weird for an object to define its own needs
and provides
but this need not be the case. It is possible to create discrete provider classes that encapsulate construction behaviour which can be injected into our target class. So instead of from this
we could say, for instance, from PersonProvider
.
This syntax allows us to create defined contracts for our classes without a lot of extra verbosity that could mask intent. It also gives us certain guarantees around type safety and compile time checking the our object graphs are correctly defined. Finally, as an added bonus, this syntax gives us a rather clean way of wiring up stubs and mocks during testing by simply swapping out our providers with stubbed providers.
Testing
On the subject of testing I was a little surprised to read "gives you testability from the ground up" and to not see any examples of how to write tests. Of course the language is still quite young and things are changing. After a bit of poking around I discovered tests are annotation based,
import Asserts;
@TestClass
every MyFirstTest is:
@Test
itIsWhatItIs(Asserts) {
var Text = "test";
Asserts.that(Text)Equals("test");
}
Running make
will run all the discovered tests in the tests/
folder. Fairly straightforward.
Other Nuances
There are few things in Wake that exist in an attempt to make code more concise, more readable and potentially drive design down a particular route.
Naming Variables
Most variables do not need to be given a name. Our Person
class above could be written in a more concise manner,
every Person is:
needs Printer, Text;
sayHi($Text) {
Printer.printLine("Hello " + $Text + " I'm " + Text + "!");
}
- We have one instance of
Printer
so we don't need to give it a variable name and can refer to it directly by its type. - The same applies for
Text
however as we have 2Text
instances we can make use of the$
symbol to avoid conflicts.
The argument here is that the type of a variable is often the most meaningful name. This is easily a very contentious point which makes sense in some situations but not necessarily in others. Thankfully its possible to give variables names which I did in the first instance in order to avoid introducing too many new concepts in one go. This approach can take you down the "thinking in types" path where you make the types more rich rather than just place holders for rich data.
Method Names
Like in Swift and Objective-C, Wake methods names don't need to follow the
sayHelloTo(Text)As($Text) {
Printer.printLine("Hello " + Text + " I'm " + $Text + "!");
}
main() {
sayHelloTo("Emma")As("James");
}
Wrapping Up
I've spent more time putting this article together than I have spent with the language but all in all there are some compelling features in Wake.
The contract based approach of needs
and provides
makes the structure of your application very clear and aids testing
The ability to reduce the volume of code by eliminating unnecessary cruft (variable names for example), where it fits, goes a step further to allowing us to create more maintainable code.
As of right now the documentation has some holes in it (for example around testing) and the language is still very young but I think its a language worth keeping an eye on.
I've only scratched the surface of the language and its future plans so I recommend, if this is the first time you're reading about Wake, you check out the site for more information - http://www.wakelang.com/