[next] [prev] [prev-tail] [tail] [up]

### 6.6 Operators

nip2’s expression syntax is almost exactly the same as C, with a few small changes. Table 6.2 lists all of nip2’s operators in order of increasing precedence. If you’ve used C, the differences are:

- C’s ?: operator becomes if-then-else, see above
- Like almost every functional language, nip2 uses square brackets for list constants (see §6.6.5), so to index a list, nip2 uses ?
- The : operator is infix list cons, see §6.7
- nip2 adds @ for function composition, see §6.6.6
- The ++ operator becomes an infix concatenation operator, again see §6.7
- nip2 adds ⋆⋆ for raise to power

The only slightly tricky point is that function application binds very tightly (only list index and class project bind more tightly). So the expression:

jim = fred 2 + 3

binds as:

jim = (fred 2) + 3

This is almost always the behaviour you want.

There are two special equality tests: === and !==. These test for pointer equality, that is, they return true if their arguments refer to the same object. These are occasionally useful for writing interactive functions.

Operator | Associativity | Description
if then else | Right | If-then-else construct
, | Left | List/complex separator
|| | Left | Logical or
&& | Left | Logical and
@ |  | Function composition (see §6.6.6)
| | Left | Bitwise or
| Left | Bitwise exclusive or
& | Left | Bitwise and
== | Left | Equal to
!= |  | Not equal to
=== |  | Pointer equal to
!== |  | Pointer not equal to
< | Left | Less than
<= |  | Less than or equal to
> |  | Greater than
>= |  | Greater than or equal to
<< | Left | Left shift
>> |  | Right shift
+ | Left | Addition
- |  | Subtraction
⋆ | Left | Multiplication
∕ |  | Division
% |  | Remainder after division
! | Left | Logical negation
~ |  | One’s complement
++ |  | Join (see §6.7)
-- |  | Unary minus
+ |  | Unary plus
(type) |  | Type cast expression
⋆⋆ | Right | Raise to power
: |  | List CONS (see §6.7)
space | Left | Function application
? | Left | List index (see §6.7)
. | Left | Class project (see §6.11)
Table 6.2: | nip2 operators in order of increasing precedence

#### 6.6.1 The real type

nip2 has a single number type for integers and real numbers. All are represented internally as 64-bit floating point values. You can use the four standard arithmetic operators (+, -, ⋆, /), remainder after integer division (%), raise-to-power (⋆⋆), the relational operators (<, <=, >, >=, ==), the bitwise logical operators (&, |, , ~), integer shift operators (<<, >>) and unary negation and positive (-, +).

Other mathematical functions are pre-defined for you: sin, cos, tan, asin, acos, atan, log, log10, exp, exp10, ceil, floor. Each has the standard behaviour.

You can use type-casts on reals. However, they remain 64-bit floating point, the range is simply clipped. Casting to unsigned short produces a 64-bit float whose fractional part has been set to zero, and which has been clipped to the range 0 to 65535. This may or may not cause rounding problems.

You can write hexadecimal number constants as 0xff.

#### 6.6.2 The complex type

Complex numbers are rather sketchily implemented. They are generally handy for representing vectors and coordinates rather than for doing arithmetic, so the range of operations is limited.

Complex constants are written as two numbers enclosed in round brackets and separated by a comma. You can use the four standard arithmetic operators (+, -, ⋆, /), raise-to-power (⋆⋆), and unary negation and positive (-, +). You can use == only of the relational operators. You can mix complex and real numbers in expressions. You can cast reals to complex and back. Use the functions re and im to extract the real and imaginary parts.

(12, 13) + 4 == (16, 13) (12, 2 + 2) == (12, 4) re (12, 13) == 12 im (12, 13) == 13

#### 6.6.3 The character type

Character constants are written as single characters enclosed in single quotes. You can use the relational operators (<, <=, >, >=, ==) to sort characters by ASCII order. You can cast a character to a real to get its ASCII value. You can cast a real ASCII value to a character. You can use the standard C escapes to represent non-ASCII characters.

(int) 'A' == 65 (char) 65 == 'A' is_digit x = '0' <= x && x <= '9' newline == '\n'

#### 6.6.4 The boolean type

The two boolean constants are written as true and false. Boolean values are generated by the relational operators. You can use the standard logical operators (&&, ||, !). You can use a boolean type as an argument in an if-then-else expression.

As with C, the logical operators do not evaluate their right-hand sides if their value can be determined just from evaluating their left-hand sides.

true && false == false true || error "boink!" == true if true then 12 else 13 == 12

#### 6.6.5 The list type

Lists are created from two constructors. [] denotes the empty list. The list construction operator (:, pronounced CONS by LISP programmers) takes an item and a list, and returns a new list with the item added to the front. As a convenience, nip2 has a syntax for list constants. A list constant is a list of items, separated by commas, and enclosed in square brackets:

12:[] == [12] 12:13:14:[] == 12:(13:(14:[])) == [12,13,14] [a+2,3,4] == (a+2):3:4:[] [2]:[3,4] == [[2],3,4]

Use the functions hd and tl to take the head and the tail of a list:

hd [12,13,14] == 12 tl [12,13,14] == [13,14]

Use .. in a list constant to define a list generator. List generators build lists of numbers for you:

[1..10] == [1,2,3,4,5,6,7,8,9,10] [1,3..10] == [1,3,5,7,9] [10,9..1] == [10,9,8,7,6,5,4,3,2,1]

List generators are useful for expressing iteration.

Lists may be infinite:

[1..] == [1,2,3,4,5,6,7,8,9 ..] [5,4..] == [5,4,3,2,1,0,-1,-2,-3 ..]

Infinite lists are useful for expressing unbounded iteration. See §6.8.

A list may contain any object:

[1,'a',true,[1,2,3]]

Mixing types in a list tends to be confusing and should be avoided. If you want to group a set of diverse objects, define a class instead, see §6.11.

Lists of lists of reals are useful for representing arrays.

As a convenience, lists of characters may be written enclosed in double quotes:

"abc" == ['a','b','c']

You can use the list index operator (?) to extract an element from a position in a list:

[1,2,3] ? 0 == 1 "abc" ? 1 == 'b'

You can use the list join operator (++) to join two lists together end-to-end.

[1,2,3] ++ [4,5,6] == [1,2,3,4,5,6]

You can write list comprehensions like this:

[x | x <- [1..]; x % 2 == 0]

This could be read as All x such that x is in [1..] and x is even, that is, the list of even numbers.

You can have any number of semicolon-separated qualifiers and each one can be either a generator (like x <- [1..]) introducing a new variable or pattern (see §6.9), or a predicate (like x % 2 == 0) which filters the generators to the left of it.

Later generators change more rapidly, so for example:

[(x, y) | x <- [1..3]; y <- [x..3]] == [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]

You can nest list comprehensions to generate more complex data structures. For example:

[[x ⋆ y | x <- [1..10]] | y <- [1..10]]

will generate a times-table.

You can use pattern-matching (see §6.9) to loop over several generators at the same time. For example:

[(x, y) | [x, y] <- zip2 [1..3] [1..3]] == [(1, 1), (2, 2), (3, 3)]

#### 6.6.6 The function type

Functions are objects just like any other. You can pass functions to other functions as parameters, store functions in lists, and so on.

You can create anonymous functions with \ (lambda). For example:

map (\x x + 2) [1..3] == [3, 4, 5]

You can nest lambdas to make multi-argument anonymous functions, for example:

map2 (\x\y x + y) [1..3] [2..5] == [3, 5, 7]

You can compose functions with the @ operator. For example, for two functions of one argument f and g:

f (g 2) == (f @ g) 2

#### 6.6.7 The image type

These represent a low-level handle to a VIPS image structure. You can make them with the vips_image builtin, and you can pass them as parameters to VIPS functions. The Image class is built on top of them, see §6.12.3.

As an accident of history, nip2 also lets you do arithmetic with them. This will probably be removed in the next version or two, so it’s best to go through the higher-level Image class.

[next] [prev] [prev-tail] [front] [up]