# Macro Cookbook
This chapter is a quick map of the macro surface. The goal is not to replace
the deeper chapters, but to make the everyday question easy:
> "I know roughly what I want to do. Which macro should I reach for?"
The macros fall into three layers:
- **Schema definition**: `attributes!`
- **Fact construction**: `entity!`
- **Query construction**: `find!`, `exists!`, `pattern!`, `pattern_changes!`,
`path!`, `and!`, `or!`, `temp!`, `ignore!`
## Define attributes with `attributes!`
Use [`attributes!`](crate::macros::attributes) to declare typed attributes once,
then reuse them everywhere else.
```rust
use triblespace::prelude::*;
mod social {
use triblespace::prelude::*;
use triblespace::prelude::valueschemas::{GenId, ShortString};
attributes! {
/// A person's display name.
"A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
/// Another person this person knows.
pub friend: GenId;
}
}
assert_ne!(social::name.id(), social::friend.id());
```
Reach for this macro when:
- you are defining a namespace or schema module
- you want attributes with stable ids and value schemas
- you want doc comments to become attribute metadata
If you already have attributes, you usually do not need `attributes!` in the
rest of the code you are writing.
## Build facts with `entity!`
Use [`entity!`](crate::macros::entity) when you want to create tribles for one
entity.
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# use triblespace::prelude::valueschemas::ShortString;
# attributes! {
# "A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
# }
# }
let alice = fucid();
let facts = entity! { &alice @
social::name: "Alice",
};
assert_eq!(facts.root(), Some(alice.id));
assert_eq!(facts.len(), 1);
```
If you omit the entity id, `entity!` derives one deterministically from the
attribute/value pairs.
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# use triblespace::prelude::valueschemas::ShortString;
# attributes! {
# "A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
# }
# }
let facts = entity! { _
@ social::name: "Alice"
};
assert!(facts.root().is_some());
assert_eq!(facts.len(), 1);
```
The macro also supports optional and repeated fields:
```rust,ignore
let aliases = ["Al", "A."];
let maybe_nickname = Some("Ace");
let facts = entity! { &alice @
social::name: "Alice",
social::nickname?: maybe_nickname,
social::alias*: aliases,
};
```
Reach for this macro when:
- you are constructing new data
- you want optional/repeated attribute ergonomics
- you want a deterministic derived id for a value object
## Match facts with `pattern!`
Use [`pattern!`](crate::macros::pattern) to turn trible-shaped structure into a
query constraint.
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# use triblespace::prelude::valueschemas::{GenId, ShortString};
# attributes! {
# "A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
# "B74AA63539354CDA47F387A4C3A8D54C" as pub friend: GenId;
# }
# }
# let mut kb = TribleSet::new();
# let alice = fucid();
# let bob = fucid();
# kb += entity! { &alice @ social::friend: &bob };
# kb += entity! { &bob @ social::name: "Bob" };
let results: Vec<Id> = find!(
friend: Id,
pattern!(&kb, [
{ alice.id @ social::friend: ?friend },
{ ?friend @ social::name: "Bob" }
])
).collect();
assert_eq!(results, vec![bob.id]);
```
Inside a pattern:
- `?name` refers to a query variable from the surrounding query
- `_?name` introduces a local helper variable scoped to that pattern
- literal expressions become equality constraints automatically
Use `pattern!` when you are querying the current contents of a `TribleSet`,
`Checkout`, or another pattern-capable source.
## Query for results with `find!`
Use [`find!`](crate::macros::find) when you want rows back.
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# use triblespace::prelude::valueschemas::ShortString;
# attributes! {
# "A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
# }
# }
# let mut kb = TribleSet::new();
# let alice = fucid();
# kb += entity! { &alice @ social::name: "Alice" };
let names: Vec<Value<_>> = find!(
name: Value<_>,
pattern!(&kb, [{ _?person @ social::name: ?name }])
).collect();
let first: &str = names[0].try_from_value().unwrap();
assert_eq!(first, "Alice");
```
There are three common shapes:
- `find!(value, constraint)` for one projected variable as a bare value
- `find!((a, b), constraint)` for tuples
- `find!((), constraint)` when you care only that matches exist
Typed projections happen in the head:
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# use triblespace::prelude::valueschemas::ShortString;
# attributes! {
# "A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
# }
# }
# let mut kb = TribleSet::new();
# let alice = fucid();
# kb += entity! { &alice @ social::name: "Alice" };
let ids: Vec<_> = find!(
person: Id,
pattern!(&kb, [{ ?person @ social::name: "Alice" }])
).collect();
assert_eq!(ids, vec![alice.id]);
```
Use `?` on a projected variable when you want conversion failures as
`Result<T, E>` instead of dropping the row.
## Ask existence questions with `exists!`
Use [`exists!`](crate::macros::exists) when you only need yes/no.
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# use triblespace::prelude::valueschemas::ShortString;
# attributes! {
# "A74AA63539354CDA47F387A4C3A8D54C" as pub name: ShortString;
# }
# }
# let mut kb = TribleSet::new();
# let bob = fucid();
# kb += entity! { &bob @ social::name: "Bob" };
let has_bob = exists!(
pattern!(&kb, [{ _?person @ social::name: "Bob" }])
);
assert!(has_bob);
```
You can also keep the typed-head form when the projection itself matters to the
check:
```rust,ignore
let has_name = exists!(
(name: Value<_>),
pattern!(&kb, [{ ?person @ social::name: ?name }])
);
```
Use `exists!(constraint)` for pure existence checks instead of
`find!((), constraint).next().is_some()`.
## Match only new results with `pattern_changes!`
Use [`pattern_changes!`](crate::macros::pattern_changes) for incremental
queries: matches are allowed to join against the full current state, but at
least one contributing trible must come from the change set.
```rust,ignore
for (work,) in find!(
(work: Value<_>),
pattern_changes!(&full, &delta, [
{ ?work @ literature::author: &shakespeare }
])
) {
// process only newly introduced matches
}
```
Reach for this macro when:
- you already have `full` and `delta`
- you want monotonic incremental processing
- `pattern!` would re-emit old matches every time
See [Incremental Queries](incremental-queries.md) for the full workflow.
## Traverse edges with `path!`
Use [`path!`](crate::macros::path) when a relationship is recursive or
variable-length.
```rust
# use triblespace::prelude::*;
# mod social {
# use triblespace::prelude::*;
# attributes! {
# "B74AA63539354CDA47F387A4C3A8D54C" as pub friend: valueschemas::GenId;
# }
# }
# let mut kb = TribleSet::new();
# let alice = fucid();
# let bob = fucid();
# let carol = fucid();
# kb += entity! { &alice @ social::friend: &bob };
# kb += entity! { &bob @ social::friend: &carol };
let results: Vec<(Id, Id)> = find!(
(src: Id, dst: Id),
path!(kb.clone(), src social::friend+ dst)
).collect();
assert!(results.contains(&(alice.id, bob.id)));
assert!(results.contains(&(alice.id, carol.id)));
assert!(results.contains(&(bob.id, carol.id)));
```
The middle part is a small regular language over attributes:
- adjacency means concatenation
- `|` means alternation
- `*` means zero or more
- `+` means one or more
- parentheses group
Example:
```rust,ignore
Use `path!` when a fixed number of `pattern!` clauses would be awkward or
impossible.
## Combine constraints with `and!` and `or!`
Use [`and!`](crate::prelude::and) when every clause must hold:
```rust,ignore
find!(
(friend: Value<_>),
and!(
pattern!(&kb, [{ ?person @ social::name: "Alice" }]),
pattern!(&kb, [{ ?person @ social::friend: ?friend }])
)
)
```
Use [`or!`](crate::prelude::or) when any branch may hold:
```rust,ignore
find!(
(alias: Value<_>),
or!(
pattern!(&kb, [{ ?person @ social::nickname: ?alias }]),
pattern!(&kb, [{ ?person @ social::name: ?alias }])
)
)
```
`or!` branches must mention the same variable set.
## Introduce helpers with `temp!`
Use [`temp!`](crate::prelude::temp) when you need a fresh variable only inside a
sub-expression.
```rust,ignore
find!(
(person: Value<_>),
temp!((friend), and!(
pattern!(&kb, [{ ?person @ social::friend: ?friend }]),
pattern!(&kb, [{ ?friend @ social::name: "Bob" }])
))
)
```
This is useful when the helper participates in joins but should not be
projected.
## Hide helpers with `ignore!`
Use [`ignore!`](crate::prelude::ignore) when a constraint needs internal
variables that should not be visible to the outer planner.
```rust,ignore
find!(
(person: Value<_>),
ignore!((friend),
pattern!(&kb, [
{ ?person @ social::friend: ?friend },
{ ?friend @ social::name: "Bob" }
])
)
)
```
This is a more specialized tool than `temp!`. Reach for it when you already
have a nested constraint that uses helper variables internally and you want to
hide them from the outer query.
## Which macro should I use?
If you are:
- defining a schema: use `attributes!`
- building facts for one entity: use `entity!`
- matching trible structure: use `pattern!`
- matching only newly added results: use `pattern_changes!`
- asking for rows back: use `find!`
- asking for a boolean: use `exists!`
- traversing recursive edges: use `path!`
- requiring all clauses: use `and!`
- allowing alternatives: use `or!`
- introducing a fresh helper variable: use `temp!`
- hiding helper variables from the outer query: use `ignore!`
From here, the best next stops are:
- [Query Language](query-language.md) for the execution model
- [Patterns & Recipes](patterns-and-recipes.md) for modeling patterns
- [Incremental Queries](incremental-queries.md) for `pattern_changes!`