In recent years, a flood of new and exciting languages have sprung up on both the Java and .NET platforms due to each platform's extensible infrastructure for executing a well documented byte code format. In the Java world, there's a lot of excitement over JRuby and Groovy- two languages that provide a high degree of flexibility when it comes to syntax and behavior that can ultimately lead to higher levels of productivity in the right scenarios. The .NET platform also has its fair share of excitement when it comes to innovative languages- indeed, Microsoft's official support of IronPython and F# give real credence the flexibility and extensibility of the CLI.
While both IronPython and F# have gained a lot of mindshare due to the weight of the entity behind them, there are a few other languages, which also target the CLI. These languages have been spearheaded by the open source community including L# (a Lisp based language for the CLI), two Ruby implementations counting IronRuby and Ruby.NET.
While a few of these innovative languages targeting the CLI are ports of existing languages (i.e. IronPython is Python for the CLI like JRuby is Ruby for the JVM), some are completely new languages with their own syntax that are merely influenced by popular languages. Such is the case with boo, which is a statically typed language targeting the CLI that is influenced by Python but isn't a direct port of Python. In fact, boo doesn't necessarily care about indentations nor does it force you to use the self keyword. Plus, boo is fundamentally a statically typed language, which is quite different from Python's type-less system.
Because of boo's built in language features and simple syntax coupled with it's statically typed nature, you can code .NET applications quicker that will most likely run as fast as if you wrote them in C#. Plus, because it's written in boo, you can reuse any existing library that targets the CLI and boo code can be reused in other CLI languages!
Developing with boo is far from scary
Born out of Rodrigo B. de Oliveira's frustration with some excessive coding constructs of C# (like having to cast types) and an inability to test running code via a shell, boo has evolved quickly into a convenient platform for building prototype GUIs, developer testing, and even games for both Window's .NET and the Mono platforms.
The easiest way to get started with boo is to fire up boo's interactive shell, aptly named booish. With booish, you can interactively enter in code snippets and start to understand how boo's syntax works.
For example, the prosaic hello world example in boo is quite simple- just use call print followed by your desired string and you're good to go.
>>> print "Hello Scary World!"
Hello Scary World!
Needless to say, I could also define the code above in a source file and execute the file via the booi command.
C:\dev\projects\acme>booi src\helloworld.boo
Hello scary world!
In fact, I can compile my helloworld.boo script into a Windows executable using booc, which compiles boo code into a valid CLI application.
C:\dev\projects\acmi>booc -out:bin/helloworld.exe src/helloworld.boo
Boo Compiler version 0.7.6.2237 (CLR v1.1.4322.2032)
C:\dev\projects\acme\bin>helloworld.exe
Hello scary world!
Boo is a statically typed language that infers a variables type. For instance, I can create a variable that holds a "Hello world" string value as so:
>>> var = "Hello world"
'Hello world'
>>> var.GetType()
System.String
Note that I didn't have to declare that var is of type string; however, because boo is statically typed, it inferred that var is a string based upon its value and accordingly set it as such.
Boo strings
Much like Python, boo adds a lot of flexibly with built-in constructs that facilitate working with strings, collections, and files to name a few.
Boo supports string interpolation via the ${} syntax and even Python style multi-line strings.
>>> adj = "scary"
'scary'
>>> print "Hello ${adj} world"
Hello scary world
Multi-line strings are quite flexible as you don't need to worry about escaping.
firstname = "Joe"
lastname = "Dirt"
accountid = "A499"
amount = "1.32"
msg = """
<person fname="${firstname}" lname="${lastname}">
<account id="${accountid}" amount="${amount}"/>
</person>
"""
Like Python, and Ruby for that matter, Regular Expressions and collections (i.e. lists and maps) are built into the language itself through syntactic shortcuts.
Regular expressions are delineated via /'s and are under the covers of type System.Text.RegularExpressions.Regex. Matching is done via Perl's =~ operator too. For example, I can create a regular expression for matching zip codes as follows:
>>> regx = /^\d{5}([-]\d{4})?$/
^\d{5}([-]\d{4})?$
>>> if '22101-4444' =~ regx:
print "yes"
Boo collections
Boo has three built-in collection types- normal arrays (which are fixed length and can only hold one fixed type), lists (which are resizable and can hold different types) and hashes (which are in essence maps that hold a collection of name, value pairs).
Arrays
Arrays are lists of the same type and can not grow beyond a predefined length. In boo they are created like so:
>>> myarray = (1,2,4,5)
(1, 2, 4, 5)
Also too, you can not add a different type to the array either.
>>> myarray.SetValue(6, 3)
>>> myarray
(1, 2, 4, 6)
>>> myarray.SetValue('test', 3)
System.InvalidCastException: Object cannot be stored in an array of this type.
at System.Array.InternalSetValue(Object value, Int32 index1, Int32 index2, Int32 index3)
at Input50Module.Main(String[] argv)
Lists
Lists are grow-able and index-able arrays that can hold various types. These are quite flexible too. Boo lists are created via brackets (['s), which is barrowed from Python and are internally of type Boo.Lang.List
>>> mylist = [1, 2, 3, 4]
[1, 2, 3, 4]
>>> mylist.GetType()
Boo.Lang.List
>>> mylist.Add('test')
[1, 2, 3, 4, 'test']
As you can see, boo lists can hold different types and new elements are added via the Add method. You can also check out what's in a list via the Contains method or even by asking a logical question:
>>> mylist.Contains(3)
true
>>> 'test' in mylist
true
Hashes
Boo hashes are containers, which hold name-value pairs; moreover, these pairs can be of different types.
>>> myhash = {".NET":"boo", "Java":"Groovy"}
{'Java': 'Groovy', '.NET': 'boo'}
Hash work via name-value pairs; consequently, if you enter in a name, you'll get its logical value. In my case, I typed in ".NET" and got back 'boo' as shown below.
>>> myhash[".NET"]
'boo'
You can also search hashes via the ContainsKey and ContainsValue methods as shown below.
>>> myhash.ContainsKey('Java')
true
>>> myhash.ContainsValue('Groovy')
true
Iteration
Boo, as in other languages like Python and Ruby, allows for easy iteration of collection types; however, blocks or closures as you've seen in Ruby or Groovy are not available.
List like iteration
Boo iteration is commonly done via the for notation as follows:
>>> mylist
[1, 2, 3, 4, 'test']
>>> for value in mylist:
... print value
...
1
2
3
4
test
>>> myint
9
>>> myarray
(1, 2, 4, 6)
>>> for i in myarray:
... print 2*i
...
2
4
8
12
Hash iteration
You can iterate over a hash as well:
>>> myhash
{'Java': 'Groovy', '.NET': 'boo'}
>>> for key in myhash:
... print key.Value
...
Groovy
boo
Note, because boo hashes store name-values as System.Collections.DictionaryEntry's you can reference both the Key and Value members.
>>> for key in myhash:
... print key.Key
...
Java
.NET
Boo Functions
Boo allows you to create functions that exist outside of a class definition, much like Python and Groovy. Boo's functions are first class (meaning they are objects themselves) and they're declared with the def keyword.
For example, the code below defines a string with the value of 'boo'. Notice that there isn't a lower built-in function like there exists in Python. Creating a lower function is easy though- just define it using the def keyword and remember to declare parameters with the as <type> phrase too.
>>> str = "Boo"
'Boo'
>>> str.GetType()
>>> lower(str)
----^
ERROR: Unknown identifier: 'lower'.
>>> def lower(val as string):
... return val.ToLower()
...
>>> print lower(str)
boo
Boo IO
Boo makes working with files a breeze- by using the using keyword, you can effectively ignore file handling constructs like close as using ensures that the file is properly disposed of.
For example, reading a local file is as easy as typing:
>>> import System.IO
>>> myfile = "SampleFile.txt"
'SampleFile.txt'
>>> using input = File.OpenText(myfile):
... for line in input:
... print line
...
Welcome to an easy
way to read files
using the most unscary language around:
BOO!
Using a function, you can recreate Groovy's getText style method as follows:
>>> import System.IO
>>> def GetText(file as string):
... retString = " "
... using input = File.OpenText(file):
... for line in input:
... retString += line
... return retString
...
>>> myfile = "SampleFile.txt"
'SampleFile.txt'
>>> assert GetText(myfile).Equals('Welcome to an easy way to read files using the most unscary language around: BOO! ')
>>>
Integration with NAnt
Boo provides the ability to script with a more expressive language in NAnt build files via the boo task. As an example of the possibilities of what you can do with boo in NAnt, check out the following build script which runs FxCop on a dll and uses boo to quickly summarize the number of Critical Errors.
<property name="fxcop.xml"
value="${build.dir}\bin\${build.config}\fxcop.xml"/>
<target name="fxcop" depends="build">
<fxcop>
<targets>
<include name="${build.dir}\bin\${build.config}\${library}" />
</targets>
<arg value="/out:${fxcop.xml}" />
</fxcop>
</target>
<target name="fxcop-analysis" depends="fxcop">
<boo>
import System.IO
fpath = Project.Properties['fxcop.xml']
numerror = 0
using input = File.OpenText(fpath):
for line in input:
if line =~ /Level="CriticalError"/:
numerror++
print("There were ${numerror} Critical Errors")
</boo>
</target>
Developer testing is a breeze with boo
Because of boo's ability to work with other CLI libraries, boo code can easily use NUnit- attributes and all.
For example, the following code uses NDbUnit, which facilitates testing with a database, and NUnit to build a simple test case that inserts seed data via NDbUnit's API and verifies the information was indeed committed to a database.
As you can see below, Boo is less verbose than normal C#- there are no semi-colons, brackets and fewer type declarations.
import NUnit.Framework
import NDbUnit.Core.OleDb
import NDbUnit.Core
import System.Data
import System
import System.Data.OleDb
[TestFixture]
class WordTest:
final CONN = "Provider=SQLOLEDB...."
final SCHEMA = "Dataset2.xsd"
final XML = "XMLFile2.xml"
fixture as OleDbUnitTest
[SetUp]
def configure():
fixture = OleDbUnitTest(CONN)
fixture.ReadXmlSchema(SCHEMA)
fixture.ReadXml(XML)
[Test]
def VerifyWordTableOle():
fixture.PerformDbOperation(DbOperationFlag.CleanInsert)
select = "select spelling from word where word.word_id = 2"
adapter = OleDbDataAdapter(select , CONN)
dta = DataSet()
adapter.Fill(dta, "word")
table = dta.Tables["word"]
for row as DataRow in table.Rows:
Assert.AreEqual("pugnacious", row[0],
"word spelling wasn't pugnacious")
For those looking for the quickest mechanism to knock out developer tests, boo is a wise choice as it supports attributes (which it seems at this point, IronPython doesn't) and therefore, boo works quite nicely with NUnit.
Static yet somewhat dynamic
Boo is a static typed language even though you don't have to explicitly declare a variable's type. Under the covers, boo infers the variables type based upon its value. For example, the following script creates a string type and then attempts to call a non-existing method.
var = "BOO"
var.lower()
If I try to compile this script, I'll get the following error:
C:\dev\projects\acme>booc -out:bin/statictypes.exe src/statictypes.boo
Boo Compiler version 0.7.6.2237 (CLR v1.1.4322.2032)
src/ statictypes.boo(3,5): BCE0019: 'lower' is not a member of 'string'.
1 error(s).
Also too, I can try and run this script and I'll get a similar error:
C:\dev\projects\acme>booi src\statictypes.boo
src\statictypes.boo(3,5): BCE0019: Boo.Lang.Compiler.CompilerError: 'lower' is not a member of 'string'.
Boo does, however, offer some flexibility when it comes to typing. Through the use of a duck type, you can defer compile time checks. When a variable is of type duck, boo will reflectively attempt to invoke a declared method. For example, if I change my script to declare var of type duck, I can compile the script without error. If I run the script directly, I'll get a different, but telling, error:
C:\dev\projects\acme>booi src\statictypes.boo
System.MissingMethodException: Method System.String.lower not found.
at System.RuntimeType.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Obje
ct[] args, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters)
at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] ar
gs)
at Boo.Lang.Runtime.RuntimeServices.Invoke(Object target, String name, Object[] args)
at StatictypesModule.Main(String[] argv)
Boo's duck typing elegantly facilitates writing tests that drive Internet Explorer like so:
>>> ie as duck =
System.Type.GetTypeFromProgID("InternetExplorer.Application")()
System.__ComObject
>>> ie.Visible = true
true
>>> ie.Navigate2("http://www.thediscoblog.com")
>>> links = ie.Document.getElementsByTagName("a")
System.__ComObject
>>> site = "http://thediscoblog.com/2007/02/04/currying-maximum-favor-with-groovy/"
'http://thediscoblog.com/2007/02/04/currying-maximum-favor-with-groovy/'
>>> for link in links:
... if(link.href.Equals(site)):
... link.click()
...
mshtml.HTMLAnchorElementClass
Note how the ie variable is typed as duck to facilitate passing specific messages to the instance, which would otherwise result in nasty errors.
After finding a specific link on a webpage, I am able to click it and then verify precise content on the resulting page.
>>> h3s = ie.Document.getElementsByTagName("h3")
System.__ComObject
>>> for h3 in h3s:
... if(h3.id.Equals("comments")):
... assert h3.innerText =~ "5 Responses"
...
mshtml.HTMLHeaderElementClass
>>> ie.Quit()
Development with boo is natural
With a relaxed syntax ,boo is a very convenient platform for doing just about anything on the .NET platform quickly. Look to boo when you need to iterate quickly when prototyping or building user interfaces. On the plus side, anything you do end up writing and compiling with boo is completely reusable with all other .NET libraries you’ve written. Whether they have been written in C#, VB.NET, or managed C++. Bottom line- boo is a wrist friendly language for the .NET platform that enables the quick construction of working code with minimal rules. Don’t be frightened-- give boo a try today!