Skip to main content

Attributes

Once you've defined an inductive type or a structure type, you can augment it with additional attributes.

For example, let's start with this LatticePoint structure type.

from int import Int
numerals Int

structure LatticePoint {
x: Int
y: Int
}

The attributes Statement

To add extra attributes to the LatticePoint type, you can define both functions and variables inside a block with the attributes keyword.

attributes LatticePoint {
// Now accessible as LatticePoint.origin
let origin = LatticePoint.new(0, 0)

// Now accessible as LatticePoint.swap
define swap(self) -> LatticePoint {
LatticePoint.new(self.y, self.x)
}
}

The first argument to a function must be named self, and it automatically get the type that we're defining attributes for.

Constants and functions defined inside an attributes block are now attributes accessible as TypeName.attribute_name. For example:

theorem swap_is_involutive(p: LatticePoint) {
p.swap.swap = p
} by {
p.swap.x = p.y
p.swap.y = p.x
}

The names for constants and functions inside an attributes block are the same as outside, except that constants in an attributes block can also have numeric names. Thus Nat.0 is the name for zero, the natural number, and Int.0 is the name for zero, the integer.

Writing the numerals Int statement lets us avoid typing Int. before every numeral.

It's okay to have multiple attributes blocks for a single type. You often want to define some attributes, prove some things about them, then define more attributes.

Operators

Every operator has a alphabetical reserved name. If you define a method of this name, the operator will work as well. For example, the + operator corresponds to the name add:

attributes LatticePoint {
define add(self, other: LatticePoint) -> LatticePoint {
LatticePoint.new(self.x + other.x, self.y + other.y)
}
}

theorem add_origin(p: LatticePoint) {
p + LatticePoint.origin = p
}

The names of the supported operators are:

// Greater than
a.gt(b) = a > b

// Less than
a.lt(b) = a < b

// Greater than or equal to
a.gte(b) = a >= b

// Less than or equal to
a.lte(b) = a <= b

// Addition
a.add(b) = a + b

// Subtraction
a.sub(b) = a - b

// Multiplication
a.mul(b) = a * b

// Division
a.div(b) = a / b

// Mod
a.mod(b) = a % b

// Negative. The unary '-' is different from the binary '-'.
a.neg = -a