Skip to content

The COOL Language

Overview

Cool is a Classroom Object-Oriented Language --- it contains a number of programming language features common in other object oriented languages (such as Java). It's small enough to be manageable for semester projects but large enough to help you to learn all of the concepts and to write non-trivial programs.

Cool is created by Alex Aiken. Here is the helloworld program written in Cool

helloworld

(* hello-world.cl *) 
class Main inherits IO { 
    main() : Object { 
        out_string("Hello, world.\n") 
    } ; 
} ; 

Reference Manual

The Cool programming language is formally specified and defined in The Cool Reference Manual, a document that you will grow to love (or hate) over the course of this semester.

Reference Compiler

Download the below pre-compiled binaries based on your machine/OS. After downloading a binary cool.something.exe, make the file executable by setting chmod a+x /where/you/download/cool.something.exe. Now you can run Cool by executing the command /where/you/download/cool.something.exe.

  • 64 bit: compiled on a 64-bit OpenSUSE Leap 42.3 machine
  • Note: this is the official version that runs on the CSE machine and we will use it to test your programs.
  • Other 64-bit versions: a, b, c, d
  • 32 bit

Cool example programs

Example: Hello, world

(* hello-world.cl *) 
class Main inherits IO { 
  main() : Object { 
  out_string("Hello, world.\n") 
  } ; 
} ;    
$ ./cool.linux.exe examples/hello-world.cl # test
Hello, world.

A more complicated example: linked list
(*
*  This file shows how to implement a list data type for lists of integers.
*  It makes use of INHERITANCE and DYNAMIC DISPATCH.
*
*  The List class has 4 operations defined on List objects. If 'l' is
*  a list, then the methods dispatched on 'l' have the following effects:
*
*    isNil() : Bool        Returns true if 'l' is empty, false otherwise.
*    head()  : Int     Returns the integer at the head of 'l'.
*              If 'l' is empty, execution aborts.
*    tail()  : List        Returns the remainder of the 'l',
*              i.e. without the first element.
*    cons(i : Int) : List  Return a new list containing i as the
*              first element, followed by the
*              elements in 'l'.
*
*  There are 2 kinds of lists, the empty list and a non-empty
*  list. We can think of the non-empty list as a specialization of
*  the empty list.
*  The class List defines the operations on empty list. The class
*  Cons inherits from List and redefines things to handle non-empty
*  lists.
*)


class List {
-- Define operations on empty lists.

isNil() : Bool { true };

-- Since abort() has return type Object and head() has return type
-- Int, we need to have an Int as the result of the method body,
-- even though abort() never returns.

head()  : Int { { abort(); 0; } };

-- As for head(), the self is just to make sure the return type of
-- tail() is correct.

tail()  : List { { abort(); self; } };

-- When we cons and element onto the empty list we get a non-empty
-- list. The (new Cons) expression creates a new list cell of class
-- Cons, which is initialized by a dispatch to init().
-- The result of init() is an element of class Cons, but it
-- conforms to the return type List, because Cons is a subclass of
-- List.

cons(i : Int) : List {
    (new Cons).init(i, self)
};

};


(*
*  Cons inherits all operations from List. We can reuse only the cons
*  method though, because adding an element to the front of an emtpy
*  list is the same as adding it to the front of a non empty
*  list. All other methods have to be redefined, since the behaviour
*  for them is different from the empty list.
*
*  Cons needs two attributes to hold the integer of this list
*  cell and to hold the rest of the list.
*
*  The init() method is used by the cons() method to initialize the
*  cell.
*)

class Cons inherits List {

car : Int;   -- The element in this list cell

cdr : List;  -- The rest of the list

isNil() : Bool { false };

head()  : Int { car };

tail()  : List { cdr };

init(i : Int, rest : List) : List {
    {
    car <- i;
    cdr <- rest;
    self;
    }
};

};

(*
*  The Main class shows how to use the List class. It creates a small
*  list and then repeatedly prints out its elements and takes off the
*  first element of the list.
*)

class Main inherits IO {

mylist : List;

-- Print all elements of the list. Calls itself recursively with
-- the tail of the list, until the end of the list is reached.

print_list(l : List) : Object {
    if l.isNil() then out_string("\n")
                else {
            out_int(l.head());
            out_string(" ");
            print_list(l.tail());
                }
    fi
};

-- Note how the dynamic dispatch mechanism is responsible to end
-- the while loop. As long as mylist is bound to an object of 
-- dynamic type Cons, the dispatch to isNil calls the isNil method of
-- the Cons class, which returns false. However when we reach the
-- end of the list, mylist gets bound to the object that was
-- created by the (new List) expression. This object is of dynamic type
-- List, and thus the method isNil in the List class is called and
-- returns true.

main() : Object {
    {
    mylist <- new List.cons(1).cons(2).cons(3).cons(4).cons(5);
    while (not mylist.isNil()) loop
        {
        print_list(mylist);
        mylist <- mylist.tail();
        }
    pool;
    }
};

};
$ ./cool.linux.exe examples/list.cl
5 4 3 2 1 
4 3 2 1 
3 2 1 
2 1 
1