(prop name ..subclauses)
  name: a sym
  subclauses: zero or more forms

Defines a property.

A property is a field combined with a "getter" method and/or a "setter" method. Those methods can't be invoked directly, but they will automatically be invoked when the field is accessed using [] or @.

The sub-clauses are an optional initializer form, followed by either or both of a (get ..body) and (set (arg) ..body) form, not necessarily in that order. Note that the get form does not have a parameter list, because it's always invoked without any arguments.

The underlying field has the same name as the property, but suffixed with :field. For example, the backing field for the property fizz might have the unqualified name fizz:field, and the qualified name Main:fizz:field. Within a getter or setter method's body, the @field shorthand can be used to access the backing field.

(defclass Example
  (prop doubled 5.5
    (get (* @field 2.0))
    (set (n) (= @field (/ n 2.0)))))

(let ob (Example))
(prn [ob 'doubled]) ; prints 11.0
(prn [ob 'doubled:field]) ; prints 5.5

(= [ob 'doubled] 21.0)
(prn [ob 'Main:doubled]) ; prints 21.0
(prn [ob 'Main:doubled:field]) ; prints 10.5

(get) is equivalent to (get @field).

(set) is equivalent to (set (x) (= @field x)).

Properties can be wrapped like normal methods. Unlike fields and constants, they don't participate in name-shadowing.