.. Mars Documentation Copyright (C) 2008 Matt Giuca 3/9/2008 .. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. .. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. .. You should have received a copy of the GNU General Public License along with this program. If not, see . .. _intro: .. Note the careful use of ..highlight for switching between the 'mars' and 'marscon' languages. 'mars' should be used for code examples, and 'marscon' for Bash and Mars console session examples. Introduction to Mars ==================== .. sectionauthor:: Matt Giuca Welcome to Mars. This document is an informal overview of the language, designed as a tutorial for programmers already familiar with other languages. Please note that there is also a :ref:`formal specification `, which is intended as a reference, not an introduction. Please be aware that Mars is a toy language, for academic research. It doesn't have many of the features you might expect from a proper programming language -- it's got just enough to prove my points. You're probably using this just to see what it's like. I don't expect you to write real programs in it. I'm going to describe the language (in this document) very informally, and admit when things are not ideal. .. note:: If I'm describing command line input, I'll use ``$`` to denote the Unix prompt (such as Bash), and ``?>`` for the Mars interactive prompt. What is Mars? ------------- Mars is a very simple imperative programming language with a catch: all of the functions and expressions are pure. That means when you call a function, it is guaranteed to have no side-effects, like mutating its arguments, much like Haskell or Erlang. But, *unlike* those other pure languages, Mars gives you all the nice features of imperative programming, like conditional statements and while loops. Mars also has some other nice features borrowed from functional programming: a strong static type system, algebraic data types, pattern-matching switch statements, and higher-order functions. Starting off ------------ Firstly, you'll want to grab the Mars compiler and build it, using Mercury (the language it is written in). This is described in the :ref:`setup ` document. Interactive mode ++++++++++++++++ .. highlight:: marscon The program :program:`mars` is executed from the command line, and is primarily used to execute Mars programs. It can also act as an interactive interpreter, like the Python one. To start off, we'll use the interactive mode. To launch this, simply run the command:: $ mars When called with no arguments, :program:`mars` defaults to interactive mode, and automatically loads the :mod:`prelude` (named after the Haskell prelude), which is the "standard library" of Mars. For example, type into the Mars prompt:: ?> print_string("Hello, world!\n") This will print out the familiar text ``"Hello, world!"``. It also prints out 0, which is the return value of the function. (All functions in Mars must return a value; there is no "Void" type. Functions that don't need to return a value, like :func:`print_string`, just return the :type:`Num` value 0). You can see the other functions available (in the prelude) by typing ``:b`` into the prompt. You might notice some arithmetic functions, such as :func:`add`, :func:`sub` and :func:`mul`. These can be used to perform simple arithmetic:: ?> add(4.2, mul(9, 2)) 22.2 Mars also has the usual binary operators (with the normal precedence and associativity) as a short-hand notation for the above functions, so you can treat the Mars interpreter like a calculator:: ?> 4.2 + 9 * 2 22.2 Hello World program +++++++++++++++++++ .. highlight:: mars Now let's write a real program. Mars programs mostly consist of function definitions. The syntax is a lot like Python, except you need type declarations. Here is the complete Hello World program:: #!/usr/bin/env mars import prelude def main() :: io Num: print_string("Hello, world!\n") return 0 .. note:: Mars programs must import the :mod:`prelude` in order to use its functions. It is legal *not* to import the prelude, but you will have access to only a very limited number of functions and types (the "built-ins"). The prelude only appears by default if you start :program:`mars` with no arguments, as a courtesy. Here we define the :func:`main` function, with no arguments. As stated above, functions need to return a value. :func:`main` just returns a :type:`Num`, 0. You may also note the special keyword :keyword:`io`, which says that :func:`main` is allowed to perform side-effects such as printing. .. highlight:: marscon Save the above program in a file, :file:`hello.mar`, and run :program:`mars` on it:: $ mars hello.mar That's the standard way to run Mars programs. .. note:: In Unix, you can also run Mars programs like any other Unix scripts, if you remember to place the line ``#!/usr/bin/env mars`` at the top, and ``chmod +x`` the file (make it executable). Consult a Unix manual for help on doing this. You can also experiment with your own programs in interactive mode, much like we did with the prelude. Just run :program:`mars` with the :option:`-i ` option:: $ mars -i hello.mar ?> main() Hello, world! 0 Now that that's all out the way, I can show you some of the features of Mars. Basic syntax ------------ .. highlight:: mars At first glance, Mars is a fairly typical statically-typed language. As stated above, it has syntax much like Python. This means that unlike other languages, which use curly braces or some other symbols to delimit blocks, Mars just uses indentation. I agree with the designers of Python that this makes the code much neater, and enforces good indentation. For example, here is how you would write an :keyword:`if` statement:: import prelude def compare_to_seven(x :: Num) :: Num: if x == 7: return 0 # x is 7 elif x > 7: return 1 # x is greater than 7 else: return -1 # x is less than 7 Note that you may optionally specify :keyword:`elif` ("else if") and :keyword:`else` clauses. If there is no :keyword:`else` clause and none of the other conditions match, the :keyword:`if` statement does nothing. .. note:: Unlike :func:`main`, :func:`compare_to_seven` does *not* feature the :keyword:`io` keyword, because it doesn't perform any side-effects (it is a "pure" function -- it just returns a value). You should not use the :keyword:`io` annotation unless you need to perform some I/O effect. Variables and statements ++++++++++++++++++++++++ .. highlight:: mars Mars will automatically infer the type of any variable assigned in the body of a function. Variables may also be explicitly declared, using the :keyword:`var` keyword. All declarations must come at the top of the function. You can use a while loop just as you might expect. Assignment statements are also quite straightforward, using the :keyword:`=` operator, much to the chagrin of my supervisor. Here is an example using local variables, while loops, and assignment statements to compute a factorial. Note that you can assign to argument variables (which will not modify the caller's copy; arguments are passed by value). :: import prelude # Compute the factorial of n. def factorial(n :: Num) :: Num: var fac :: Num fac = 1 while n > 1: fac = fac * n n = n - 1 return fac .. highlight:: marscon Readers are reminded that the easiest way to test functions is to run :program:`mars` in interactive mode, like this:: $ mars -i factorial.mar ?> factorial(5) 120 .. highlight:: mars Of course, you can also write recursive functions, such as this much more natural definition of :func:`factorial`:: import prelude # Compute the factorial of n. def factorial(n :: Num) :: Num: if n > 1: return n * factorial(n - 1) else: return 1 If you want to write a program around the function, you can wrap it in a :func:`main` function like this:: def main() :: io Num: print_value(factorial(5)) print_string("\n") return 0 .. note:: :func:`print_value` can print out any value in its canonical form. It does this by calling ``print_string(show(value))``. :func:`show` converts the value into a string, and :func:`print_string` prints it out. Switch statements +++++++++++++++++ .. highlight:: mars Mars features a :keyword:`switch` statement, similar to the one found in C-like languages. It's like an :keyword:`if` statement, but lets you make decisions across a range of values. For example:: def foo(x :: Num) :: io Num: switch x: case 0: print_string("x is zero.\n") case 4: print_string("x is four.\n") case _: print_string("I'm not sure what x is.\n") return 0 Only :keyword:`case` statements are allowed inside :keyword:`switch` statements. The :keyword:`switch` statement picks the first matching :keyword:`case` statement and executes it. Note the final ``case _`` -- this is the default case, and it is *mandatory* for switching over a :type:`Num`. If you don't want to write a default case, either put a call to :func:`error` here, if it should never occur, or a :keyword:`pass` statement, if you want to do nothing in that case. .. note:: Unlike C and many C-derived languages, there is no :keyword:`break` statement for finishing a case. Mars simply doesn't allow fall-through. The :keyword:`switch` statement is actually a lot more powerful than this, as we'll see later. Arrays ++++++ .. highlight:: mars Mars allows the definition of array values using the array literal notation:: var a :: Array(Num) a = [1, 1.5, 2, 2.5, 3] This creates arrays of fixed length. Note that the :type:`Array` data type must be given an argument, which specifies the type of the array elements. All elements of an array must have the same type. .. highlight:: marscon Strings in Mars are actually just arrays of numbers, representing the characters' ASCII values:: ?> "hello" [104, 101, 108, 108, 111] Whenever you deal with strings, you should be sure that the array values are all whole numbers within the range 0 -- 255 inclusive. Values above 127 should be treated as UTF-8 code units. Most string-handling functions will raise an error if either of these conditions are not met. .. highlight:: mars Arrays of arbitrary size may also be created with the :func:`array` function. There are several primitive functions which operate on arrays: :func:`array_ref`, :func:`array_replace`, :func:`array_concat` and :func:`array_length`. Because Mars is pure, there is no array assignment primitive --- :func:`array_replace` is used to make a copy of an array with an updated cell. For example, this function adds 1 to each element of an array:: def array_increment(a :: Array(Num)) :: Array(Num): var i :: Num i = 0 while i < array_length(a): a = array_replace(a, i, array_ref(a, i) + 1) i = i + 1 return a .. highlight:: marscon :: ?> array_increment([1,2,3]) [2, 3, 4] .. note:: Because :func:`array_replace` is non-destructive, it can be inefficient to run it in a loop. The Mars compiler has sophisticated optimizations to avoid unnecessary copying, but they aren't effective in all cases. See :ref:`destructive` for a detailed discussion on this optimization process. Types ----- .. highlight:: marscon You can find out the type of any variable, function or expression in the interactive mode using the ":t" command, like this:: ?> :t 4 4 :: Num ?> :t [1, 2, 3] [1, 2, 3] :: Array(Num) ?> x = 4 ?> :t x x :: Num ?> :t add add :: (Num, Num) -> Num The last example shows the type of a function, in functional notation (which is covered in the next section). This means the :func:`add` function takes two numbers, and returns a number. Functional notation ------------------- .. highlight:: mars If a function only has a single return statement in it, it can be summarized using the :keyword:`=` symbol in the header. For example, the following function:: def increment(n :: Num) :: Num: return n + 1 Can be written more succinctly as:: def increment(n :: Num) :: Num = n + 1 A function may be written without any arguments or argument parentheses. This isn't actually a function at all --- it is called a "computable global constant", as once evaluated, it is just a value which doesn't need to be called. However, its body may contain a complex computation, not just a simple value. :: def ten :: Num = 4 + 6 .. highlight:: marscon :: ?> ten 10 .. highlight:: mars Note that this is quite different from a function *with zero arguments*, like our :func:`main` functions above. You could alternatively define ten as a "nullary function" like this:: def ten() :: Num = 4 + 6 .. highlight:: marscon :: ?> ten Note that the value "ten", in this case, is an unevaluated function. It requires empty parentheses to evaluate it properly:: ?> ten() 10 .. highlight:: mars Functions in Mars are *first-class*. That means you can treat any function as if it were a value, as we saw with the function :func:`ten` above. For example, we can define :func:`increment` in this (silly) way:: def increment(n :: Num) :: Num: var f :: (Num, Num) -> Num f = add return f(n, 1) Note that because we are not calling it directly, we must use the full name :func:`add` rather than the short-hand ``+`` operator. In the above code, we assign the function :func:`add` to a local variable *f*. Later, we call *f*, which calls whatever function we put in it earlier. Note the type of *f*, :type:`(Num, Num) -> Num`, which means "a function expecting two numbers and returning one numbers." Using this technique, functions can be passed as arguments, returned as results, and stored in data structures. Mars also supports explicit *currying* -- you can pass fewer arguments to a function than it expects, and Mars will create a new function that expects the remaining arguments. For example:: def increment(n :: Num) :: Num: var f :: Num -> Num f = add(n, ...) return f(1) This version of :func:`increment` passes just one argument to :func:`add` (*n*); the ``...`` signifies that the remaining argument will come later. Then, when we call *f*, we need supply only the one remaining argument, 1. You may curry as many or as few arguments as you like at a time, and even curry an already-curried function. This leads to the most elegant solution for :func:`increment`:: def increment :: Num -> Num = add(1, ...) Here, :func:`increment` is a computable global constant, the value of which is a function. The body curries 1 into :func:`add` and returns the curried function. Now, any call to :func:`increment` will complete the call to :func:`add` and be added to 1. IO annotation ------------- As a pure functional language, Mars normally doesn't allow functions to perform side-effects. Mars distinguishes between two types of side-effects: mutation (such as updating objects and global variables) and input/output (such as reading and writing from files. We have covered the basics of mutation in the previous section. Here, we focus on input/output effects. The most obvious input/output effect is printing text to the console. A function like :func:`print_string` is clearly impure: when you call it, you are not interested in its result (it always returns 0), but rather the effect that it has on the state of the computer (displaying text to the screen). More serious side-effects could include reading and writing files or databases, manipulating graphics, and communicating over the network (although Mars doesn't directly support such features). This is contrast with a "pure" function like :func:`factorial`, which doesn't *do* anything other than returning a result. There would be no point to programming if you couldn't do effects at some point or another, so rather than banning them altogether, we try to avoid accidental side-effects by forcing you to declare your intention to use them. That is why all functions that do side-effects must have the :keyword:`io` in their header. This should help you structure your code accordingly: try to avoid using :keyword:`io` wherever possible, and you will find it simplifies your code. For example, don't have functions read input from the user; have them take the input as arguments and write a wrapping :keyword:`io` function that reads the input. .. note:: There are times when you want to quickly print out some value while you are debugging your code, and it would be annoying if you had to put :keyword:`io` on all of your code just to do that. That's why we provide the :func:`trace` function in the :mod:`debug` module -- you can use it to print text to standard error without being in an :keyword:`io` context. Just try to use it for debugging and not as part of your actual program. There is a subtle interaction between :keyword:`io` and the functional style described above: you cannot store an :keyword:`io` function in a normal function variable. If you could, you could pass that variable as an argument to a non-:keyword:`io` function and then it could perform illegal side-effects! Mars has a separate type for :keyword:`io` functions, which has the form :type:`a -> io b` (meaning, "a function that takes arguments *a*, does some input/output effects, and returns type *b*"). This is completely incompatible with the normal :type:`a -> b` function type: you can either deal with :keyword:`io` functions or regular functions. For example, the following code prints "x" to the screen, by passing a curried version of :func:`put_char` to the :func:`do_effect` function:: def print_x() :: io Num: var f :: Num -> io Num f = put_char('x', ...) return do_effect(f) def do_effect(f :: () -> io Num) :: io Num: return f() .. note:: Computable global constants (procedures without arguments) may not be declared :keyword:`io`. If a procedure performs input or output, like :func:`main`, it must be a nullary function instead, with empty parentheses. This allows the caller to explicitly specify when to execute the procedure. This is okay:: def x() :: io Num: This is illegal:: def x :: io Num: # ERROR: Constant declared as io. User-defined types ------------------ .. note:: Do not import the :mod:`prelude` module for the examples in this section, or the names will conflict with names already defined there. .. highlight:: mars Mars programs may define their own algebraic data types (or discriminated unions; data types with several alternatives). These data types may be recursive. For example, one may define a linked-list of numbers:: type NumList: Cons(head :: Num, tail :: NumList) Nil This type has two alternatives. A :type:`NumList` may be ``Nil`` (representing the empty list), or ``Cons`` of a :type:`Num` and a :type:`NumList`. For example, the value ``Cons(4, Nil)`` represents a list with one element, while ``Cons(4, Cons(5, Cons(6, Nil)))`` represents a list with three. The :keyword:`switch` statement in Mars can be used for pattern matching over user-defined types. The :keyword:`case` statement may bind variables given as arguments to a type's alternative. This example finds the length of a :type:`NumList`:: def intlist_length(l :: NumList) :: Num: switch l: case Nil: return 0 case Cons(_, xs): return intlist_length(xs) + 1 .. highlight:: marscon :: ?> intlist_length(Cons(4, Cons(5, Cons(6, Nil)))) 3 .. highlight:: mars .. note:: The default case (``case _``) is not required if you explicitly match all possible alternatives of the data type, as in the example above. It is preferred that you do not have a default case, as the compiler will give you an error if you missed any cases. However, for switching over a :type:`Num`, you are required to have a default case, as you can't possibly have a case for every number. User-defined types may have type parameters, so they can store any different type (much like arrays). :: type List(a): Cons(head :: a, tail :: List(a)) Nil This definition is found in the prelude; it defines a linked-list type which can hold elements of any type. Compiling a Mars program into an executable ------------------------------------------- You can compile a Mars program into a native executable using :program:`marsc`. See its :ref:`man page ` for details.