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.Comparable
Gosu 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 20Gosu 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.StringIt 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.