ixa 2.0.0-beta2

A framework for building agent-based models
Documentation
# Migration to ixa 2.0

## Properties in the new Entity system

In the new Entity system, properties work a little differently.

### The Old Way to Define a Property

Previously a property consisted of two distinct types: the type of the
property's _value_, and the type that identifies the property itself.

```rust
// The property _value_ type, any regular Rust datatype that implements
// `Copy` and a few other traits, which we define like any other Rust type.
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum InfectionStatusValue {
    Susceptible,
    Infected,
    Recovered,
}

// The type identifying the property itself. A macro defines this as a ZST and
// generates the code connecting it to the property value type above.
define_person_property_with_default!(
    InfectionStatus,      // Property Name
    InfectionStatusValue, // Type of the Property Values
    InfectionStatusValue::Susceptible // Default value used when a person is added to the simulation
);
```

The only entity in the old system is a person, so there's no need to specify
that this is a property for the `Person` entity.

### The New Way to Define a Property

In the new system, we combine these two types into a single type:

```rust
// We now have an `Entity` defined somewhere.
define_entity!(Person);
// This macro takes the entire type declaration itself as the first argument.
define_property!(
    enum InfectionStatus {
        Susceptible,
        Infected,
        Recovered,
    },
    Person,
    default_const = InfectionStatus::Susceptible
);
```

If you want to use an existing type for a property, or if you want to make the
same type a property of more than one `Entity`, you can use the `impl_property!`
variant:

```rust
// The downside is, we have to make sure the property type implements all the
// traits a property needs.
#[derive(Copy, Clone, Debug, PartialEq, Serialize)]
pub enum InfectionStatus {
    Susceptible,
    Infected,
    Recovered,
}

// Implements `Property\<Person>` for an existing type.
impl_property!(
    InfectionStatus,
    Person,
    default_const = InfectionStatus::Susceptible
);
```

(In fact, the _only_ thing `define_property!` does is tack on the `derive`
traits and `pub` visibility to the type definition and then call
`impl_property!` as above.)

The crucial thing to understand is that the value type _is_ the property type.
The `impl Property\<Person> for InfectionStatus`, which the macros give you, is
the thing that ties the `InfectionStatus` type to the `Person` entity.

For details about defining properties, see the `property_impl` module-level docs
and API docs for the macros `define_property!`, `impl_property!`,
`define_derived_property!`, `impl_derived_property!`, and
`define_multi_property!`. The API docs give multiple examples.

### Summary

| Concept                     | Old System                                                                                                                     | New System                                                                                                           | Notes / Implications                                                                |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| **Property Type Structure** | Two separate types: (1) value type, and (2) property-identifier ZST.                                                           | A single type represents both the value and identifies the property.                                                 | Simplified to a single type                                                         |
| **Defining Properties**     | Define a normal Rust type (or use an existing primitive, e.g. `u8`), then use a macro to define the identifying property type. | Define a normal Rust type, then use `impl_property` to declare it a `Property\<E>` for a particular `Entity` `E`.     |                                                                                     |
| **Entity Association**      | Implicit—only one entity ("person")                                                                                            | Every property must explicitly specify the `Entity` it belongs to (e.g., `Person`); Entities are defined separately. |                                                                                     |
| **Default Values**          | Provided in the macro creating the property-identifier type.                                                                   | Same but with updated syntax; default values are per `Property\<E>` implementation.                                   |                                                                                     |
| **Using Existing Types**    | A single _value_ type can be used in multiple properties—including primitive types like `u8`                                   | Only one property per type (per `Entity`); primitive types must be wrapped in a newtype.                             | Both systems require that the existing type implement the required property traits. |
| **Macro Behavior**          | Macros define the property’s ZST and connect it to the value type via trait impls.                                             | Macros define "is a property of entity `E`" relationship via trait impl. No additional synthesized types.            | Both enforce correctness via macro                                                  |

## Global property validator errors

Global-property validators are client code, so they should return
`Result<(), Box<dyn std::error::Error + 'static>>` instead of constructing
`IxaError` values directly. Ixa wraps any returned validator error in
`IxaError::IllegalGlobalPropertyValue` when a global property is set or loaded.

## New Entities API: How Do I...?

We will use `Person` as an example entity, but there is nothing special about
`Person`. We could just as easily define a `School` entity, or an `Animal`
entity.

### Defining a new `Entity`

Use the `ixa::define_entity!` macro:

```rust
define_entity!(Person);
```

This both _declares_ the type `Person` and implements the `Entity` trait for it.
If you want to implement the `Entity` trait for a type you have _already
declared_, use the `impl_entity!` macro instead:

```rust
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
pub struct Person;
impl_entity!(Person);
```

These macros automatically create a type alias of the form
`MyEntityId = EntityId\<MyEntity>`. In our case, it defines the type alias
`PersonId = EntityId\<Person>`.

### Adding a new entity (e.g. a new person)

Adding a new entity with only default property values by passing the entity type
directly:

```rust
let person_id = context.add_entity(Person).unwrap();
```

(This example assumes there are no required properties, that is, that every
property has a default value.)

Adding a new entity with property values using the `with!` macro:

```rust
let person_id = context.add_entity(with!(Person, Age(25), InfectionStatus::Infected)).unwrap();
```

The `with!` macro takes the entity type as its first argument, followed by any
property values. The properties must be distinct, of course, and there must be
a value for every "required" property, that is, for every (non-derived)
property that doesn't have a default value.

Adding a new entity with just one property value:

```rust
let person_id = context.add_entity(with!(Person, Age(25))).unwrap();
```

### Getting a property value for an entity

```rust
// The compiler knows which property to fetch because of the type of the return value we have specified.
let age: Age = context.get_property(person_id);
```

In the rare situation in where the compiler cannot infer which property to
fetch, you can specify the property explicitly using the turbo fish syntax. We
recommend you always write your code in such a way that you can use the first
version.

```rust
let age = context.get_property::<Person, Age>(person_id);
```

### Setting a property value for an entity

```rust
// The compiler is always able to infer which entity and property using the `EntityId` and type
// of the property value.
context.set_property(person_id, Age(35));
```

### Index a property

```rust
// This method is called with the turbo-fish syntax, because there is no way for the compiler to infer
// the entity and property types.
context.index_property::<Person, Age>();
```

It is not an error to call this method multiple times for the same property. All
calls after the first one will just be ignored.

### Subscribe to events

The only difference is that now we use the
`PropertyChangeEvent\<E: Entity, P: Property<E>>` and
`EntityCreatedEvent\<E: Entity>` types.

```rust
pub fn init(context: &mut Context) {
    context.subscribe_to_event(
        move |context, event: PropertyChangeEvent<Person, InfectionStatus>| {
            handle_infection_status_change(context, event);
        },
    );

    context.subscribe_to_event(move |context, event: PropertyChangeEvent<Person, Alive>| {
        handle_person_removal(context, event);
    });
}
```