I don't have time to write a book.
You probably don't have time to read a book.
So, how about a quick introduction to Gosu and how to get productive in it, instead?
Gosu is an imperative, statically typed programming language for the JVM. It is designed to be a easy for Java developers to pick up. If falls between Groovy, which is an imperative, dynamically typed language (with optional type annotations) and Scala, which is a functional-ish, statically typed language.
It's nothing like Clojure, which is a Lisp variant and, therefore, insane.
Why learn Gosu, given all the alternatives out there? Here are three reasons:
That's it. You can now run Gosu using the scripts in the bin directory of the gosu distribution.
Gosu has an online playground that lets you poke around with the language before you install it.
Gosu doesn't have the concept of a public static void main(String[] args) method, because that's just
crazy.
Instead, it has programs, like Ruby or Python, which are bits of Gosu code in a file ending in the
.gsp extension.
Here is hello_word.gsp:
print( "Hello World!")
Easy enough, right?
You run this program like so:
Thumper carson$ gosu hello_world.gsp
Hello World!
Thumper carson$
You can use the gw.lang.cli.CommandLineAccess class to get at arguments passed to your Gosu program.
My favored approach right now is to lay out a project like this:
/proj_name
/src - Gosu classes, XSD's, WSDL's, etc.
/lib - 3rd party jars
/bin - Gosu programs
/test - A directory for your tests
build.vark - An Aardvark build file.
Given this layout, you can add the following to the top of your gosu programs in the /bin directory:
classpath "../src,../lib"
This classpath statement will add the src directory and all jars in the
lib directory to the classpath when this program is launched, so you can run your project without any crazy command line setup or secondary scripts:
Thumper carson$ gosu bin/my_prog.gsp
Hello Gosu Projects!
The classpath statement is discussed more on the Misc page
Gosu supports the usual math operators, and they work on the major primitive and non-primitive numeric types, such as
BigDecimal and BigInteger
Gosu supports the usual comparison operators:
== - Object equality=== - Instance equality>, <, etc. - Standard comparison semantics, works with java.lang.ComparableGosu supports the usual logical operators, and you can use either the english style not,
and and or, or the C-style equivalents. I use the english style operators.
In Gosu, you typically do not need to declare the type of variables:
var x = 10
print( x + x ) // prints 20
Gosu will infer the type of x to be int.
If you want to explicitly type the variable, you put the type annotation after the variable name:
var x : int = 10
print( x + x ) // prints 20
The if statement works just like it does in every other sane language:
var x = 10
if( x > 5 ) {
print( "x was greater than 5")
}
The for statement is similar to other languages:
var aList = {"a", "b", "c"} // declare a List<String>
for( x in aList ) {
print( x )
}
The for statement works with both arrays and classes that implement
java.lang.Iterable. Note that x is explicitly typed to
java.lang.String above, via type inference.
Sometimes you want the index of an iteration. You can use this syntax to get it:
var aList = {"a", "b", "c"}
for( x in aList index i ) {
print( i + ":" + x )
}
i will be the zero-based index of the current loop of the iteration.
Sometimes you want the iterator for the loop. You can use this syntax to get it:
var aList = {"a", "b", "c"}
for( x in aList iterator it ) {
it.remove()
}
it can be used to safely remove items, for example.
Note that the for loop is null safe: no NPE occurs if aList is null, rather
the loop simply does not execute.
Gosu has while, do/while and switch statements, all of which work like you
would expect.
Gosu allows you to determine the runtime type of a value with the typeof operator:
var x = "This is a String"
var t = typeof x
print( t.Name ) // prints java.lang.String
It the code above, the type of t is
gw.lang.reflect.IType, which is the Gosu Type System's equivalent of java.lang.Class
Gosu has a way to test if an object is an instance of a type, akin to Java's instanceof keyword: typeis.
Here is an example:
var x : Object
x = "A String"
if( x typeis String ) {
print( "x is a String with length " + x.length )
}
One interesting thing about the typeis operator is that how it interacts with logical operators and the
if statement. In the code above, note that x was not cast to
String, but within the if statement the length property on
String was used. Gosu automatically downcast x to String after the
typeis expression, so no casting was necessary.
Gosu supports properties, which are a bit like public fields in Java, but they allow you to associate logic with the reading and writing operations. We will cover property definition below, but this is how you access them:
var p = new MyGosuPersonClass()
p.Name = "Joe"
print( "The name of this person is ${p.Name}")
Properties have some interesting and useful characteristics that will become apparent over time (e.g. they are both an
rvalue and an lvalue.)
Gosu automatically converts get/set methods in Java classes into properties, so the following Java class:
public class MyJavaPersonClass {
String _name;
public String getName() {
return _name;
}
public void setName( String s ) {
_name = s;
}
}
Can be used like so:
var p = new MyJavaPersonClass()
p.Name = "Joe"
print( "The name of this person is ${p.Name}")
If you wish to make a null-safe call to a method or property, you can prefix the '.' operator with a question mark, '?':
var x : String = null
print( x?.length ) // prints "null"
Gosu classes are defined with much the same syntax as other programming languages. You can define classes inside a
Gosu program, or in a file ending with the .gs extension. Here is a basic class:
uses java.util.List
class SampleClass {
var _names : List<String> // a private class variable, which is a list of Strings
// A public constructor
construct( names : List<String> ) {
_names = names
}
// A public function
function printNames( prefix : String ) {
for( n in _names ) {
print( prefix + n )
}
}
// A public property getter, making 'Names' a read-only property
property get Names() : List<String> {
return _names
}
}
The above code demonstrates the following features:
uses statement, which is identical to the import statement in Java, and makes a
class (or package of classes) available for use without qualification.
var keyword, just like local variables. Class variables default
to private access. You can declare them to be static as well.
construct keyword. This constructor allows you to declare new
instances ofSampleClass like so:
var c = new SampleClass({"joe", "john", "jack"})
Constructors default to public access.
function keyword. This function takes a String argument,
and returns no value, so no return type declaration is necessary. It can be invoked like so:
var c = new SampleClass({"joe", "john", "jack"})
c.printNames("* ")
property and get keywords. This property returns a
list of strings. It can be invoked like so:
var c = new SampleClass({"joe", "john", "jack"})
print( c.Names )
Exposing a field as a property is a common pattern, so there is a short hand syntax for it:
class SampleClass {
var _names : List<String> as Names
// A public constructor
construct( names : List<String> ) {
_names = names
}
// A public function
function printNames( prefix : String ) {
for( n in _names ) {
print( prefix + n )
}
}
}
The 'as Names' bit exposes the
_names field as both a readable and writeable a property. If you want the property to be read only, you can add the
readonly modifier after the as keyword.
Gosu supports names arguments and default parameter values to help smooth out APIs. Let's say you wanted to make the
argument to printNames() optional, with a default value of "> ". You could change the
declaration to:
// A public function
function printNames( prefix : String = "> ") {
for( n in _names ) {
print( prefix + n )
}
}
And now invoke it like so:
var c = new SampleClass({"joe", "john", "jack"})
c.printNames() // No argument is necessary, it will use the default value of "> "
Additionally, Gosu allows you to use named arguments when you are working with non-overloaded methods on Gosu classes:
var c = new SampleClass({"joe", "john", "jack"})
c.printNames(:prefix = "* ")
This can be used to clarify code, so you don't end up with stuff like this:
someMethod(true, false, null, false, true) //bwah?
someMethod( :enableLogging = true, :debug = false,
:contextObject = null, :trace = false,
:summarizeTiming = true) //Oh, I see
Gosu classes can extend other classes and implement interfaces, just like in Java, using the extends and
implements keywords respectively.
One interesting additional feature of Gosu is the ability to delegate the implementation of an interface to a class variable:
uses java.lang.Runnable
class MyRunnable implements Runnable {
//A delegate, exposed as the Impl property
delegate _runnable represents Runnable
property get Impl : Runnable {
return _runnable
}
property set Impl( r : Runnable ) {
_runnable = r
}
}
MyRunnable does not declare a run() method, like
Runnable requires. That's because the delegate field
_runnable is implementing the method for it:
var x = new MyRunnable()
x.Impl = new Runnable() {
function run() {
print("Hello, Delegation")
}
}
x.run() // prints "Hello, Delegation"
Delegates give you a convenient way to favor composition
over inheritance.