What is Q?

Q is a new programming language designed and implemented by Q Software Solutions.

Q combines the best of object-orientation with ideas from functional programming to produce a language well-suited for the engineering of precision software for large and small systems.

Why a new language?

Several existing languages came close to meeting our requirements, but none of them was exactly right. It was not possible to extend one of these languages to meet our needs - a good language is defined not just by what it provides, but by what it excludes.

The syntax of Q closely resembles that of Eiffel. Many of the ideas embodied in Q were drawn from Eiffel, Haskell and Python. Almost as many came from a multitude of niche languages that we studied. Other ideas were the result of our own in-house design work.

The design of Q

Our aim was and is to keep the language as small and consistent as possible. We based the design of Q on a few basic principles:

Design By Contract

Without a clear specification of what a software component should do, we cannot hope to re-use it successfully in large, mission-critical applications.

Bertrand Meyer's Design By Contract allows each routine to be specified by its preconditions and postconditions, each loop to be specified by its variant and invariant, each class to be specified by its class invariant, and complex code fragments to be specified by checks.

Our extensive experience of Design By Contract in Eiffel has convinced us of its value to the software development process. In Q, we go beyond Eiffel's Design By Contract by providing for the runtime checking of quantifiers (e.g. "for each" and "for some").

Command-Query Separation

Imagine what the world would be like if things could change just by looking at them!

Yet many programminlanguages allow just that - for a query on the state of an object to change the state of that object.

Q enforces command-query separation - to enhance the readability of reliability of the code. The syntax and semantics of Q have been designed so that command-query separation is both elegant and efficient.

Programming-in-the-small based on functions

Pure functional programming enables us to reason about the behaviour of our software. It provides the most straightforward way to formally reason about the correctness of our applications.

Pure functions are used in Q for programming-in-the-small. The low-level data types (Integers, Characters, etc) are purely functional, or applicative. User-written classes can just as easily be made purely functional.

Programming-in-the-large based on objects

Object-orientation offers a powerful basis for managing complexity in very large applications. It allows for a natural implementation of the user interface. It is also well-suited to persistent storage, and certain performance-bound applications.

Q fully supports object-orientation with multiple inheritance, polymorphism, and constrained genericity.

More about Q

Q allows the programmer to move freely between the functional and object-oriented paradigms. This often requires no more than the insertion of a single keyword.

We have taken care to integrate numerics into the framework of Q. Extension classes allow a new numeric type to "slot in" to the existing class hierarchy.

Enumerated types are cleanly supported in Q.

Q features automatic garbage collection. The programmer need not be concerned with storage management or reference counting.

External software written in C can be seamlessly mixed with Q code, allowing easy integration of third-party C libraries.

The low-level Q types merge seamlessly into their implementation as a sequence of bits. This allows a programmer to develop at any level from system software to fully-abstract data types.

The Q toolset includes a pretty-printer, a short tool (to extract a class interface) and a flat tool (to flatten the inherited features of a class into the class text).

What does "Hello World" look like in Q?

class Hello
   creation make
     do
       print("Hello World!" \n)
     end
endclass

To run this, put it in a file hello.q and type:

q hello

some "real Q"

At first an example on how to interface with C

class Unix_file_system_commands

   is
      File_common

   public  -- file and Directory manipulations
      -- questions is if error messages are helpful while using this
in programs
      -- maybe another higher layer should cope with those conditions
2000-07-20 (FD)
      -- probably with exception handling this error message can be
changed to some
      -- more useful usage

      make_hard_link(from: Mutable_string; to: Mutable_string)
         do
            reset_error_code;
            if file_info.exists_file(from) then
               if not file_info.exists_file(to) then
                  c_make_hard_link(from, to);
               else
                  error.print_string("File ")
                  error.print_mutable_string(to);
                  error.print_string(" does exist, can not make
link.");
               endif;
            else
               error.print_string("File ")
               error.print_mutable_string(from);
               error.print_string(" does not exist, can not make
link.");
            endif
         end

      make_symbolic_link(from: Mutable_string; to: Mutable_string)
         do
            reset_error_code;
            if file_info.exists_file(from) then
               if not file_info.exists_file(to) then
                  c_make_symbolic_link(from, to);
               else
                  error.print_string("File ")
                  error.print_mutable_string(to);
                  error.print_string(" does exist, can not make
link.");
               endif;
            else
               error.print_string("File ")
               error.print_mutable_string(from);
               error.print_string(" does not exist, can not make
link.");
            endif
         end

      unlink(pathname: Mutable_string)
         do
            reset_error_code
            if file_info.exists_file(pathname) then
               c_unlink(pathname);
            else
               error.print_string("File ")
               error.print_mutable_string(pathname);
               error.print_string(" does not exist, can not unlink");
            endif
         end

      remove_file(pathname: Mutable_string)
         do
            unlink(pathname)
         end

      change_name(from: Mutable_string; to: Mutable_string)
         -- rename unconditionally, if to exists and permissions allow
         -- all will gone
         do
            reset_error_code;
            if file_info.exists_file(from) then
               c_rename(from, to);
            else
               error.print_string("File ")
               error.print_mutable_string(from);
               error.print_string(" does not exist, can not rename");
            endif
         end

   public  -- directory manipulation

      make_directory(pathname: Mutable_string)
         do
            reset_error_code;
            if file_info.exists_file(pathname) then
               error.print_mutable_string(pathname);
               error.print_string(" exists, cannot make a new
directory");
            else
               c_make_directory(pathname);
            endif
         end

      remove_directory(pathname: Mutable_string)
         require
            is_directory: file_info.is_directory
         do
            reset_error_code;
            c_remove_directory(pathname);
         end

      change_directory(to: Mutable_string)
         do
            reset_error_code;
            c_change_directory(to);
         end

      current_working_directory: Mutable_string
         is
            cwd_name.from_c(c_current_working_directory);
         end

   public  -- general facility for removing

      remove(path_name: Mutable_string)
         -- feature to remove `path_name', regardless of what
path_name points too
         -- if it is a file, just remove the file, if it's a directory
remove all directory entries,
         -- if necessary decent further down the file-hierachy, after
deleting all entries, delete finally
         -- the directory itself.
         do
            file_info.update(path_name);
            if file_info.is_updated then
               if file_info.is_directory then
                  fsc := new Unix_file_system_commands;
                  fsc.initialize;
                  owd := fsc.current_working_directory;
                  fsc.change_directory(path_name);
                  fsc.open_directory((".").mutable_string);
                  loop until not fsc.is_directory_open
                     fsc.next_directory_entry;
                     if fsc.is_directory_open then
                        fsc.remove(fsc.directory_entry);
                     endif
                  endloop;
                  error.print_string("owd = ");
                  error.print_mutable_string(owd);
                  error.print_string(", will remove ");
                  error.print_mutable_string(path_name);
                  error.print_newline;
                  fsc.change_directory(owd);
                  fsc.remove_directory(path_name);
               else
                  error.print_string("removing ");
                  error.print_mutable_string(path_name);
                  error.print_newline;
                  remove_file(path_name);
               endif
            else
               error.print_string("Could not gather information about
");
               error.print_mutable_string(path_name);
               error.print_newline;
            endif;
         end

   public  -- features to read in all the entry of a directory

      inode: Integer

      directory_entry: Mutable_string

      is_directory_open: Boolean

      open_directory(pathname: Mutable_string)
         do
            -- old_actual_file_name := file_info.actual_file_name;
            file_info.update(pathname);
            if file_info.is_directory then
               reset_error_code;
               c_open_directory(pathname);
               if successful then
                  is_directory_open := true;
               endif
            else
               error.print_mutable_string(pathname)
               error.print_string(" is not a directory, cannot open
it");
            endif
            -- file_info.update(old_actual_file_name);
         ensure
            successful_implies_directory_opened: successful implies
not is_directory_closed
         end

      next_directory_entry
         require
            open: is_directory_open
         do
            reset_error_code
            c_next_directory_entry;
            if not dirent.is_null then
               directory_entry := new Mutable_string;
               directory_entry := directory_entry.from_c(c_direntry);
               if directory_entry.is_equal((".").mutable_string) or
directory_entry.is_equal(("..").mutable_string) then
                  -- ignore current working dir and parent
                  next_directory_entry;
               endif
               -- inode := c_inode;
            else
               if error_code /= 0 then
                  error.print_string("something went wrong in
next_entry_directory" \n)
               else
                  close_directory;
               endif
            endif
         end

      rewind_directory
         -- name? move_to_first_directory_entry
         -- start_directory. Don't know but the result is that
         -- we restart reading all the contents of the directory
         require
            open: is_directory_open
         do
            c_rewind_directory;
         end

      close_directory
         -- after finish
         require
            open: is_directory_open
         do
            c_close_directory;
            if successful then
               is_directory_open := false;
            else
               error.print_string("something went wrong while trying
to run close_directory" \n)
            endif
         end

   private

      ufi: Unix_file_info

      fsc: Unix_file_system_commands

      owd: Mutable_string

   private  -- Pointer for of type struct dirent.
      -- to use with the *_directory features

      dirent: Pointer;  -- struct dirent*

      dir_ptr: Pointer;  -- DIR*

   private  -- C implementation for the *_directory features;

      c_open_directory(pathname: Mutable_string)
         -- open a directory for getting all the file and directory
names in it
         external C_EMBED
         source
            "char *dir_name = (char*) ", `pathname.storage`, ";", \n
   "         DIR *dir = opendir(dir_name);", \n
   "         if (dir == NULL) {", \n
   "            ", `error_code`, " = errno;", \n
   "         }", \n
   "         ", `dir_ptr`, " = (_q_PointerV) dir;"
         end

      c_next_directory_entry
         -- get next directory entry (file or directory)
         external C_EMBED
         source
            "DIR *dir = (DIR*) ", `dir_ptr`, ";", \n
   "         struct dirent *dirent_ptr = readdir(dir);", \n
   "         if ((dirent_ptr == NULL) && (errno != 0)) {", \n
   "            ", `error_code`, " = errno;", \n
   "         }", \n
   "         ", `dirent`, " = (_q_PointerV) dirent_ptr;"
         end

      c_direntry: Pointer
         -- extract the name of the entry from the `dirent_ptr`
         external C_EMBED
         source
            "struct dirent *dirent_ptr = (struct dirent*) ", `dirent`,
";", \n
   "         Q_RETURN (_q_PointerV) dirent_ptr->d_name;"
         end

      c_inode: Integer
         -- extract the inode number of the entry from the
`dirent_ptr`
         -- do not know if that is useful information
         external C_EMBED
         source
            "struct dirent *dirent_ptr  = (struct dirent*) ",
`dirent`, ";", \n
   "         Q_RETURN (_q_IntegerV) dirent_ptr->d_ino;"
         end

      c_rewind_directory
         external C_EMBED
         source
            "DIR *dir = (DIR*) ", `dir_ptr`, ";", \n
   "         rewinddir(dir);"
         end

      c_close_directory
         external C_EMBED
         source
            "DIR *dir = (DIR*) ", `dir_ptr`, ";", \n
   "         closedir(dir);"
         end

   private  -- file manipulation

      c_make_hard_link(from: Mutable_string; to: Mutable_string)
         external C_EMBED
         source
            "char *from = (char*) ", `from.storage`, ";", \n
      "      char *to = (char*) ", `to.storage`, ";", \n
      "      int result = link(from, to);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_make_symbolic_link(from: Mutable_string; to: Mutable_string)
         external C_EMBED
         source
            "char *from = (char*) ", `from.storage`, ";", \n
      "      char *to = (char*) ", `to.storage`, ";", \n
      "      int result = symlink(from, to);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_unlink(pathname: Mutable_string)
         external C_EMBED
         source
            "char *pathname = (char*) ", `pathname.storage`, ";", \n
      "      int result = unlink(pathname);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_rename(from: Mutable_string; to: Mutable_string)
         external C_EMBED
         source
            "char *from = (char*) ", `from.storage`, ";", \n
      "      char *to = (char*) ", `to.storage`, ";", \n
      "      int result = rename(from, to);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_make_directory(pathname: Mutable_string)
         external C_EMBED
         source
            "char *pathname = (char*) ", `pathname.storage`, ";", \n
      "      int result = mkdir(pathname, ACCESSPERMS);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_remove_directory(pathname: Mutable_string)
         external C_EMBED
         source
            "char *pathname = (char*) ", `pathname.storage`, ";", \n
      "      int result = rmdir(pathname);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_change_directory(to: Mutable_string)
         external C_EMBED
         source
            "char *to = (char*) ", `to.storage`, ";", \n
      "      int result = chdir(to);", \n
      "      if  (result != 0) {", \n
      "         ", `error_code`, " = (_q_IntegerV) errno;", \n
      "      }"
         end

      c_current_working_directory: Pointer
         external C_EMBED
         source
            "char *cwd = NULL;", \n
      "            char *getcwd_result = NULL;", \n
      "      size_t size = PATH_MAX; /* arbitrary start length */", \n
      "      do {", \n
      "         cwd = gc_malloc (size);", \n
      "         errno = 0;", \n
      "         getcwd_result = getcwd (cwd, size);", \n
      "         if (!getcwd_result){", \n
      "            if (errno == ERANGE){", \n
      "               /* size was too small */", \n
      "               gc_free(cwd);", \n
      "               size *=2;", \n
      "            } else {", \n
      "               abort (); /* should not occur */", \n
      "            }", \n
      "         }", \n
      "      } while (!getcwd_result);", \n
      "", \n 
      "      Q_RETURN (_q_PointerV) cwd;"
         end

endclass

As you can see it is remarkable seamless and clear to read, we think every other way is simpyl to tedious and well generating tons of code is not really that well understandable

Now here's some "pure" Q code

class String_splitter

   public

      initialize
         do
            reset
            set_delimiter((" ").mutable_string)
            delimiter.extend('\t')
            delimiter.extend('\n')
         end

      set_delimiter(pattern: Mutable_string)
         do
            delimiter := pattern.twin
         ensure
            delimiter_set: delimiter.is_equal(pattern)
         end

      work_on(str: Mutable_string)
         do
            reset
            on_this := str.twin
         end

      after: Boolean
         is
            index > on_this.count
         ensure
            after_contraint: Result implies index > on_this.count
         end

      reset
         do
            index := 1;
         ensure
            was_reset: index = 1
         end;

      item: Character
         is
            on_this.item(index)
         ensure
            proper_item: Result = on_this.item(index)
         end

      next
         -- get the next element
         local
            i: Integer
            done: Boolean
         do
            done := false
            skip_delimiters
            last_mutable_string := new Mutable_string
            loop until after or done
               if not delimiter.has(item) then
                  last_mutable_string.extend(item);
                  index := index + 1;
               else
                  done := True
               endif
            endloop
         end

      skip_delimiters
         -- skip chars which are elements of `delimiter'
         local
            done: Boolean
         do
            done := false
            loop until after or done
               if delimiter.has(item) then
                  index := index + 1
               else
                  done := True
               endif
            endloop
         end

      last_mutable_string: Mutable_string;

      last_string: String
         is
            last_mutable_string.string
         end

   private

      delimiter: Mutable_string;

      on_this: Mutable_string;

      index: Integer;

      error: Standard_error;

endclass

I'd argue this is easy to understand code