Learning: Boo and DSL Part 2 – Basic Boo Syntax

large In part one we looked at what Boo is and got some tools for working with Boo. Now in part two we will familiarize us a little with the language. This is not going to be a tutorial on programming in general, but an introduction to the basics of the Boo language that will get you started, and then we will build on that in the next parts of the series. The goal is to see a lot of code and practical examples.

Significant whitespace

In C# you defines control blocks with curly brackets { } and in VB with an explicit “End …” closure statement. Like in Python the indentation of the line defines the control block by default in Boo; the thought is that we indent anyway, so it might as well mean something:

import System.Windows.Forms

class HelloWorld(Form):
    def constructor():
        button = Button(Text: "Click Me!", Dock: DockStyle.Fill) 
        button.Click += do(sender, e): MessageBox.Show("Hello World")
        self.Controls.Add(button)

form = HelloWorld()
Application.Run(form)

The control block is initiated with a colon, and the level of indentation defines the scope. Note that there is a difference between tabs and spaces, so if your code looks correctly indented and you still get a compile error, check that you haven’t mixed tabs and spaces.

Boo does however offer optional whitespace ignorance with explicit closure statement by switching a compiler switch, that allows you to use the explicit closure “end”.

Variables

In Boo everything is implicitly static typed at compile time, opposite Python where everything is implicitly duck typed and type specific operations aren’t resolved until runtime. Boo still has a nice dynamic feel to it though with type inference:

import System
	
def ReturnRandom(): 
    if DateTime.Now.Second % 2 == 0: 
        return 4
    else:
        return "hello"

variable = ReturnRandom() 
And you still have the option of getting compiler error by specifying the type:
variable as string = 4 // will not compile

Boo does automatic casting:

obj as object
obj = 5
number as int = obj // object is cast to int automatically

And you can explicitly do duck typing with the “duck” type – duck is a type like int or object, but differs as Boo will no attempt to resolve methods or operations compile time, but instead convert them into methods that will resolve at runtime, which could be handy when dealing with COM for instance. If it walks like a duck …. But it is a matter of trust: The compiler trusts that you know what you are doing – if not it will fail! So don’t give up type safety unless you really need to, or maybe while doing a POC or such:

import System.Threading

def CreateInstance(progid):
	type = System.Type.GetTypeFromProgID(progid)	
	return type()	

ie as duck = CreateInstance("InternetExplorer.Application")
ie.Visible = true // to provoke error try: ie.MadeUp = true
ie.Navigate2("http://boo.codehaus.org")

Web server part 1: Classes, fields and properties

Lets try to create an application that does something useful like a web server.

First we create a class and import the libraries that we will need, and a default empty constructor, just to get us started:

namespace Devtalk

import System
import System.Net
import System.IO

class BooWebserver:
    def constructor():   
        pass

You define a method or function with def in this case a constructor, which is explicitly denoted with constructor rather than with the class name like we see in C#. Because of the significant whitespace, empty declarations can be a bit of a problem – therefore you use the keyword pass to indicate that this declaration is empty, like in this case an empty constructor.

Now lets add some fields and properties and a constructor that takes a few arguments:

class BooWebserver:
  
    private _serverPath as string
    public ServerPath:
        get:
            return _serverPath
        set:
            _serverPath = value
  
    [Property(Prefix)]
    _prefix as string
  
    [Getter(Listener)]
    _listener as HttpListener
    
    def constructor(serverPath,prefix):
        _serverPath = serverPath
        _prefix = prefix

So we have have basically three ways to add properties: The manual one with get/set like we see with ServerPath in the above; The automatic property like in C# 3.0, which we see with Prefix; and finally automatic read- or write only properties marked with either “Getter” or “Setter” – the Listener is therefore read only. We can set visibility with the same keywords as in C#.

We don’t need to specify type on the arguments of the constructor, but we would like to have the type safety in this case, so we ask for them as strings:

  def constructor(serverPath as string, prefix as string):
      _serverPath = serverPath
      _prefix = prefix

Web server part 2: Conditionals

Now we want to add some meat to the server. Lets add a method to start and run the web server:

  def StartServer():
      if _prefix.Length <= 0:
          print "Prefix has not been set"
      if _serverPath.Length <= 0:  
          print "Path has not been set"
      if not Directory.Exists(_serverPath):
          print "Path does not exist"
      
      _listener = HttpListener()
      _listener.Prefixes.Add(_prefix)
      _listener.Start()

      while true:
          context = _listener.GetContext()
          file = Path.GetFileName(context.Request.RawUrl)
          fullPath = Path.Combine(_serverPath, file)
          extension = Path.GetExtension(context.Request.RawUrl)
          mimetype = GetMimeType(extension)
      
          if File.Exists(fullPath) and mimetype is not null:
              context.Response.AddHeader("Content-Type", mimetype)
              bytes = File.ReadAllBytes(fullPath)
              context.Response.OutputStream.Write(bytes, 0, bytes.Length)
              context.Response.OutputStream.Flush()
              context.Response.Close()
          else:
              context.Response.StatusCode = 404
              context.Response.Close()

In Boo we do if/else pretty much like in C#, but notice that you do not have a switch or select case, so you would instead do this:

  private def GetMimeType(extension):
      if extension == ".htm" or extension == ".html":
          return "text/html; charset=UTF-8"
      elif extension == ".jpg":   
          return "image/jpg"
      elif extension == ".gif":
          return "image/gif" 
      elif extension == ".png":
          return "image/png"
      elif extension == ".css":
          return "text/css; charset=UTF-8"

The elif keyword differs from if in that it can only exist following an if and the expression is only validated if the preceding expression validates to false, thereby allowing to build a hierarchy of expressions to save processing time compared with sequential if statements.

Boo also has a clever way of making one exception to a rule with the unless keyword:

number = 4
unless number == 5: 
    print "number is not equal to 5."

You can also do a statement with a modifier:

print "might make code more readable" unless currentLine.Length > 60 

But before we forget, we should try run our server. If you want you can download the source in the related links below.

server = BooWebserver("""c:\www\""", """http://localhost/""")
server.StartServer()

Point the serverpath to a folder on your computer, and put a .htm file in there, and request the file in your browser (if you have another web server running you should disable it).

Loops

We saw the while loop in the web server, but lest have a look at the other loops. Lets create a little windows app that lets us search in the nice little boo examples that comes with the distribution. First we set up a little quick and dirty UI to do the work: a textbox to type in the search phrase, a textbox for results and a search button. We hook the search button up the the search method:

namespace Devtalk

import System
import System.IO
import System.Windows.Forms

class FindInBooExamples(Form):
  
  private _searchTextBox as TextBox
  private _resultTextBox as TextBox
  private _searchButton as Button
  private _searchPath as string
  private _fileMask as string = "*.boo"
  
  def constructor(path as string):  
      _searchTextBox = TextBox(Dock:DockStyle.Top)
      _resultTextBox = TextBox(Multiline:true, Dock:DockStyle.Fill, ScrollBars:ScrollBars.Vertical, WordWrap:false)
      _searchButton = Button(Text: "Search", Dock:DockStyle.Bottom)
      _searchPath = path
      _searchButton.Click += Search
      self.Controls.Add(_searchTextBox)
      self.Controls.Add(_searchButton)
      self.Controls.Add(_resultTextBox)
  
  def Search(sender, e as System.EventArgs):  
      for file in GetFiles(_searchPath, _fileMask):
          SearchFile(file)

Boo does not have a classic C style for loop, and the for loop you see is actually mostly like the foreach loop in C#. But Boo does have the range function instead, that allows an equivalent syntax as the “normal” for loop:

for number in range(10): 
    continue if number % 2 == 0
    print number

The range statement can be called in three ways:

  • range(end)
  • range(start, end)
  • range(start, end, step)

And notice the continue statement which skips the print part of the loop, if the condition – the number is equal – is true.

Getting back to the help search application, we need to create the two methods that we omitted in the first code. Let us start with the GetFiles method: It needs to be a recursive function that runs through child folders and finds all the files, so that we can search through them, this could look something like this:

  def GetFiles(directory as string, fileMask as string) as string*:  
      folders = Directory.GetDirectories(directory) 
      for folder in folders: 
          for file in GetFiles(folder, fileMask): 
              yield file 
      for file in Directory.GetFiles(directory, fileMask): 
          yield file

Nothing tricky in that. The return type string* is Boos way of signifying a generic enumerable (T*) so in this case IEnumerable of string, and the yield keyword lets you return multiple values, just like in C#.

Finally we just need to search the file, thus implementing the SearchFile method:

  def SearchFile(file as string):
      for index, line as string in enumerate(File.OpenText(file)):
          _resultTextBox.Text += "${file}(${index}): ${line}" + Environment.NewLine if line =~ _searchTextBox.Text

This final method makes use of one of Boos build in functions: enumerate. What this does is simply do a running count of the lines in the file, and enumerating them along the way, while we loop through the lines, so that we can show the line number of the match.

Conclusion

Now that we have seen some code and covered some of the basics, we should be well equipped to move forward with some of the more advanced features of Boo, and in the next part of this series I will do just that.

Related Links

Learning: Boo and DSL Part 1 – Getting Started

largeOne of the things that are said to help you become a better developer, is to learn a new language. Even  if you never use the language in your everyday working life, it will help you better understand the languages that you are working in, and maybe make you think out of the box.

So I have for some time wanted to get started with a new language. At the same time I have been very eager to look in to meta-programming and internal DSLs, and in that connection it seems that Boo is a very interesting language. Also Boo is a CLR language, which I would prefer for my first new language.

Therefore I have decided that I will learn Boo and when I am feeling confident in Boo, I will write an internal DSL for a not yet specified purpose – I am not quite sure yet what is possible to do, so I will wait a little with decisions on the DSL part.

What is Boo?

Boo is an Open Source, object oriented statically typed language for the CLR with a python inspired syntax, and was started in 2004 by Rodrigo B. de Oliveira. Its is currently in version 0.8.2, but even if its not yet in a version 1.0, it is actually a very stable and mature language that you could use in production.

The main philosophy in Boo is to let the compiler do a lot of work to help the language get a dynamic feel with automatic variable declaration, type inference and type casting. At the same time it leaves the option open to be more specific to get static compile time error checking.

Boo also offers some functional composition in that functions are first class citizens, and can be used as return values, as arguments, stored in variables or as objects. Boo has first class generators, that is constructs capable of producing more than one value when used in an iteration like a loop (“return a for a in apples if a.IsRipe”).

Boo also supports Duck Typing which lets you give up the type safety of static typing, and accepting any object that just has the appropriate methods and properties resolved at runtime. Boo also has extension methods and extension properties.

One of the most interesting features of Boo is the extensible compilation pipeline, that lets you tap in to the compilation and modify it or add features. You could add routines to check coding conventions, write out reports or transform code. Things that you would only be able to do in C# with a post compilation tool as PostSharp, and only to a certain extend. In Boo you could for instance automagically transform a given class to a singleton, just be adding an attribute like so:

[singleton] 
class MySingleton: 
     pass

PostSharp would not be able to do this, as the instance property would be created to late. This really opens up for a lot of possibilities and you are only limited by your understanding of the compiler inner workings and the feature set.

Boo has whitespace significance like python, but does have the option of turning on a “Whitespace Agnostic” mode where you can terminate an expression with “end”, which should limit the problems you could run into.

What Do I Need to Get Started?

Basically if you want to get to know Boo, all you need is the latest distribution of Boo. Included in the bin folder you will find the executable “Booish” which is a command line interpreter of Boo, that allows you to try out the language and syntax in an easy way.

image 

For real development we are going to need an IDE, and we have two options.

A lot of Boo developers swear to SharpDevelop, and if you get the latest version it comes with Boo and is ready for use out of the box. It has the advantage seen from the DSL development approach that it is a lot easier to add code completion and syntax highlighting, which in Visual Studio would require writing a lexer and a parser; In SharpDevelop you can do it with an XML file.

But on the other hand Visual Studio is a nice mature IDE, and with BooLangStudio you get Boo and integration to the Visual Studio environment. People have complained that the project is instable and only an alpha release, but so far I haven’t experienced any problems

For now I will stick with Visual Studio as force of habit, but I might change my mind when I get to the DSL part of this endeavor.

Now that we have seen what Boo is, and have the prerequisites in place, part 2 of the series will focus on getting to know the syntax. Please subscribe to the feed.

Related Links: