.. Mars Documentation Copyright (C) 2008 Matt Giuca 11/12/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 . .. _ref-statements: ********** Statements ********** .. sectionauthor:: Matt Giuca Statements are executable lines or blocks of code, which appear inside procedures. The Mars execution model is, when evaluating a procedures, to execute each statement, in sequence. The body of a :ref:`procedure ` is comprised of a sequence of statements. Some statement forms (such as :ref:`while `) contain a nested sequence of statements. Statements are divided into two categories: basic statements (simple single-line statements) and compound statements (statements which contain nested statements or affect control flow). .. productionlist:: statement: `basic_statement` | `compound_statement` Basic statements ================ A basic statement is a single line statement, which doesn't alter control flow. .. productionlist:: basic_statement: `expression_stmt` : | `assignment_stmt` : | `field_update_stmt` : | `pass_stmt` .. _ref-expression-statement: Expression statements --------------------- An expression statement consists of a single expression: .. productionlist:: expression_stmt: `expression` NEWLINE Executing this statement simply evaluates the expression, and throws away its result. The only effect of this statement are the side-effects of the expression's evaluation. (Typically the expression is a function call, with side-effects). .. _ref-assignment-statements: Assignment statements --------------------- An assignment statement is used to bind or re-bind a local variable or variables to a new value: .. productionlist:: assignment_stmt: `pattern` "=" `expression` NEWLINE Executing this statement evaluates the expression, and matches the expression's value against the pattern using the :ref:`pattern matching ` algorithm defined below. This has the effect of binding any variables mentioned in the pattern. Any side-effects of the expression's evaluation take place during the execution of the statement. The expression's type must equal the pattern's type (which includes matching the types of any variables that have already been declared or bound), or the compiler MUST reject the program. It must be statically provable that all possible values are handled by the pattern, or the compiler MUST reject the program. This means that it is not legal to use number literal patterns, and matching a user-defined data type is only allowed if the type has a single constructor. An assignment statement of the form ``pattern = expression`` is equivalent to the :ref:`switch statement `:: switch expression: case pattern: pass Examples of assignment statements follow. A simple assignment simply assigns the value of an expression to a local variable:: x = 7 An assignment can deconstruct a value if the type has only one constructor. This is commonly used to access the values of a tuple:: p = Pair(1, 2) Pair(fst, snd) = p The deconstruction can include recursive patterns and underscores can be used to avoid binding or matching:: q = Pair(Pair(1, 2), Pair(3, 4)) Pair(_, Pair(x, y)) = q Lastly, a non-binding assignment is equivalent to a simple :ref:`expression statement `:: _ = print_string("Hello\n") .. note:: Deconstructing assignment statements are currently not supported in the interactive prompt. Only simple (variable or non-binding) assignment statements may be executed in that environment. .. _ref-field-update-statements: Field update statements ----------------------- A field update statement is used to destructively modify a named field of a user-defined type: .. productionlist:: field_update_stmt: `primary` "." `lower_identifier` "=!" `expression` : NEWLINE .. warning:: Mars is, fundamentally, a pure language. This means that no statement can modify an existing value or global state, only assign local variables (and perform input/output, if the statement appears in an I/O context). The field update statement breaks that purity by modifying a given value and all of its aliases. It should not be considered part of the language semantics, and is not intended to be used by most code (think of it as an "unofficial feature" -- the "``!``" in the syntax is designed as a warning to this effect). It is provided to increase performance in certain situations. Use at your own risk. See :ref:`ref-field-replace-expr` for a pure way to update fields. A field update statement of the form ``obj.field =! e`` modifies *obj* **and all values aliased to it**, replacing field *field* with the value of *e*. The same compile-time and runtime rules as :ref:`field reference ` expressions apply. The expression *e* MUST have the same type as the type of the parameter named *field* in any constructor of the type of *obj*. Executing this statement causes *obj* to be evaluated first. If the constructor used to construct *obj* has a parameter named *field*, then that paremeter is mutated to have the value of *e*. If the constructor used to construct *obj* does not have a parameter named *field*, then a runtime error occurs, terminating the program. Note that *obj* is evaluated before *e*. If *obj* does not have an appropriate constructor, it is unspecified whether *e* is evaluated or not. .. highlight:: marscon For example:: ?> x = Cons(1, Nil) ?> y = x ?> x.head =! 2 ?> x.tail =! Cons(2, Nil) ?> x Cons(2, Cons(2, Nil)) ?> y Cons(2, Cons(2, Nil)) ?> Nil.head =! 1 Runtime Error: List instance has no field 'head' .. highlight:: mars .. index:: statement: pass .. _pass: The :keyword:`pass` statement ----------------------------- The :keyword:`pass` statement does nothing: .. productionlist:: pass_stmt: "pass" NEWLINE It is useful in the body of a procedure or compound statment which does nothing, for syntactic reasons. (At least one statement is required, so to have a body which does nothing, a :keyword:`pass` statement is used as a dummy no-op statement). Compound statements =================== Compound statements are statements with nested statement blocks, or which affect control flow in some way: .. productionlist:: compound_statement: `return_stmt` : | `if_stmt` : | `while_stmt` : | `switch_stmt` .. index:: statement: return .. _return: The :keyword:`return` statement ------------------------------- The :keyword:`return` statement terminates the current procedure, returning a computed value: .. productionlist:: return_stmt: "return" `expression` NEWLINE The expression is evaluated, and any side-effects of evaluating the expression take place during the execution of the :keyword:`return` statement. The current procedure then terminates. The result of the entire evaluation of the procedure is the value of the expression. The expression's type must equal the procedure's declared return type, or the compiler MUST reject the program. .. note:: :keyword:`return` is not a basic statement, because it alters control flow. This means, for example, it doesn't make sense for it to be used at the command-line. .. index:: statement: if keyword: else .. _if: .. _elif: .. _else: The :keyword:`if` statement --------------------------- The :keyword:`if` statement conditionally executes a sequence of statements: .. productionlist:: if_stmt: "if" `expression` ":" NEWLINE : INDENT `statement`+ DEDENT : ( "elif" `expression` ":" NEWLINE : INDENT `statement`+ DEDENT )* : [ "else" ":" NEWLINE : INDENT `statement`+ DEDENT ] The expression on the first line is called the "condition". The condition's type must be :type:`Num`, or the compiler MUST reject the program. The first nested block of statements is called the "then" clause. If one or more :keyword:`elif` parts are supplied, then their conditions are called the "elif" conditions, and their corresponding nested blocks of statements are called the "elif" clauses. If the optional :keyword:`else` part is supplied, then the final nested block of statements is called the "else" clause. The condition is evaluated, and any side-effects take place immediately. If the value is not 0, the "then" clause is executed. If the value is 0, then the conditions of the "elif" clauses are tested in the same way, and the clause of the first successful "elif" condition is executed. If no "elif" condition succeeds, the "else" clause, if supplied, is executed. The :keyword:`elif` part is syntactic sugar for nested :keyword:`if` and :keyword:`else` clauses. For example, the code:: if a: return w elif b: return x elif c: return y else: return z is syntactic sugar for:: if a: return w else: if b: return x else: if c: return y else: return z .. index:: statement: while .. _ref-while: .. _while: The :keyword:`while` statement ------------------------------ The :keyword:`while` statement repeatedly executes a sequence of statements as long as a condition is true: .. productionlist:: while_stmt: "while" `expression` ":" NEWLINE : INDENT `statement`+ DEDENT The expression on the first line is called the "condition". The condition's type must be :type:`Num`, or the compiler MUST reject the program. On each iteration, the condition is evaluated, and any side-effects take place immediately. If the value is 0, execution of the while loop halts, and execution continues with the statement following it. If the value is not 0, the nested block of statements is executed, and then the while statement is executed again. .. index:: statement: switch .. _ref-switch-statement: .. _switch: .. _case: The :keyword:`switch` statement ------------------------------- The :keyword:`switch` statement is used to select from a number of alternatives, and also unpack values of types with several alternatives: .. productionlist:: switch_stmt: "switch" `expression` ":" NEWLINE : INDENT `case`+ DEDENT case: "case" `pattern` ":" NEWLINE : INDENT `statement`+ DEDENT The expression is first evaluated, and any side-effects take place immediately. The expression's value is then matched against each case's pattern (in the order the cases appear) using the :ref:`pattern matching ` algorithm defined below, until a match **succeeds**. After any variable binding (which may be caused by a successful match), the statements inside the successful case are executed. It must be statically provable that all possible values are handled by at least one case statement, or the compiler MUST reject the program. This can be proven either by having a wildcard pattern (underscore or variable), or by matching all alternatives of a user-defined data type. Note that the effects of variable binding outlast the :keyword:`switch` statement. Their binding is like an :ref:`assignment statement `. .. _ref-pattern-matching: Pattern matching ================ Pattern matching is an algorithm for matching a *value* with a :token:`pattern`. It is used on the left-hand side of :ref:`assignment statements ` and cases inside :ref:`switch statements `. .. productionlist:: pattern: "_" : | `variable` : | [ "-" ] `num_literal` | `char_literal` : | `constructor` [ "(" [ `pattern_list` ] ")" ] pattern_list: `pattern` ("," `pattern`)* The result is either **failure** or **success**. **Failure** means the case statement in question is rejected, and another one is tried. **Success** may be accompanied by *variable binding*; that is, one or more variables may be defined and initialised, or assigned a new value if they already exist. Pattern matching is defined by the following rules: * If a pattern is ``_``, the match **succeeds** unconditionally, with no variables bound. * If a pattern is a :token:`variable`, *x*, then the match **succeeds** unconditionally. The variable *x* is bound or re-bound to the value being matched. If the variable has been declared or already bound, it must have the same type as the value, or the compiler MUST reject the program. If variable *x* has not been declared, then this binding has the effect of declaring the variable, with the same type as the value. This declaration has the scope of the procedure. Subsequent references to the variable name, even after this :keyword:`switch` statement, must honour this declaration and its type. * If a pattern is an :token:`num_literal_expr` (or negative number literal), then the value must have type :type:`Num`, or the compiler MUST reject the program. If the value is equal to the given number or character literal, then the match **succeeds**, with no variables bound. Otherwise, it **fails**. * If a pattern is a :token:`constructor` name, optionally with parameter patterns, then the value must have the same type as the :token:`typedef` containing the constructor, and the pattern must have the same arity as the constructor, as defined there [#p1]_. If the value was constructed using the named constructor, all of the value's parameter values are matched against the pattern's parameter patterns. If all sub-matches **succeed**, then the match **succeeds**, with all variables bound in the sub-matches being bound. If any sub-match **fails**, or the value was constructed with a different constructor, the match **fails**. .. note:: The pattern matching algorithm is similar to a specialised version of the unification algorithm in mathematical logic. For a general description of this algorithm, see `Unification `_ on Wikipedia. .. [#p1] For a pattern to have the same arity as a constructor, it must match the constructor declaration exactly. Parameter-less constructors must have parameter-less patterns, and nullary constructors (with empty parentheses) must have nullary patterns. This is due to the very different semantics of parameter-less and nullary functions. See :ref:`Procedure header `.