Coding style

Introduction

One unfortunate disadvantage of rulebased programming is that rules can sometimes cooperate in unwanted ways.

One example of how rules can produce unwanted results is the rule a*0=0. This would always seem to be true. However, when a is a vector, like a:={b,c,d} , then a*0 should actually return {0,0,0} , that is, a null vector. The rule a*0 -> 0 actually changes the type of the expression from a vector to a integer! This can have severe consequences when other functions using this expressions as an argument expect a vector, or even worse, have a definition of how to work on vectors, and a different one for working on numbers.

This chapter intends to desribe the coding style and conventions applied in Yacas in order to make sure the engine always returns the correct result. This is an attempt at fending off such errors by combining rule-based programming with a clear coding style which should make these mistakes impossible.

Types

Types of expressions can be of the following form: either something is a atomic, like a or 1.1 (which is also numeric), or an expression is a compound expression, like a*b or Sin(a) . In this case, the types are "*" and "Sin" respectively.

It is assumed that the operators working on arguments, like Sin or *, always have the same properties regardless of the arguments. The Taylor series expansion of Sin(a) will be the same regardless of whether a is a real number, complex number or even a matrix. The same trigonometric identities should hold for Sin, regardless of the type of a, too.

If a function is defined which does not adhere to these rules when applied to another type, a different function should be defined.

By default, if a variable has not been bound yet, it is assumed to be a number. If it is in fact a more complex object, like a vector, then you can declare a to be an 'incomplete type' vector, using Object("IsVector",a). This subexpression will evaluate to a if and only if a is a vector at that moment of evaluation. Otherwise it returns unevaluated, and thus stays an incomplete type.

So this means the type of a variable is numeric unless otherwise stated by the user, using the "Object" command. No rules should ever work on incomplete types. It is just meant for delayed simplification.

The topic of implicit type of an object is important, since many rules often implicitly need to be able to assume something is a number.

Ordering rules

Rules specify that a specific expression will evaluate to another (probably simpler) expression only if a certain condition is met. If no rule applies to an expression, it is returned unevaluated. This scheme thus uses evaluation to simplify an expression.

The implementor of a rule set can specify the order in which rules should be tried. This can be used to let the engine try more specific rules before trying less specific rules.

The example mentioned in the introduction would be solved by a*b (b a vector) -> return vector of each component multiplied by a. a*0 -> 0 So vector multiplication would be tried first. The ordering of the precedence of the rules in the standard math scripts is currently: