PropertyDamage.Sequence (PropertyDamage v0.1.0)
View SourceRepresents a command sequence that may contain parallel branches.
A sequence consists of three parts:
prefix: Commands executed sequentially before any branchingbranches: Optional list of parallel branches (each branch is a list of commands)suffix: Commands executed sequentially after branches merge
Linear Sequences
A linear (non-parallel) sequence has branches: nil and all commands in prefix:
%Sequence{prefix: [cmd1, cmd2, cmd3], branches: nil, suffix: []}Use Sequence.linear/1 as a convenience constructor:
Sequence.linear([cmd1, cmd2, cmd3])Branching Sequences
A branching sequence has commands before the branch point in prefix,
parallel branches in branches, and commands after merge in suffix:
%Sequence{
prefix: [cmd1, cmd2],
branches: [[cmd3a, cmd4a], [cmd3b, cmd4b]],
suffix: [cmd5, cmd6]
}Use Sequence.branching/3 as a convenience constructor:
Sequence.branching(
[cmd1, cmd2], # prefix
[[cmd3a, cmd4a], [cmd3b, cmd4b]], # branches
[cmd5, cmd6] # suffix
)Execution Semantics
- Execute
prefixcommands sequentially - If
branchesis not nil: a. Fork projection state b. Execute each branch in parallel (or interleaved) c. Merge results and check for linearizability - Execute
suffixcommands sequentially
Ref Constraints
- Refs created in
prefixcan be used in any branch - Refs created in one branch CANNOT be used in another branch
- Refs created in branches CAN be used in
suffix(after merge)
Summary
Functions
Add a new branch to the sequence.
Append a command to the end of the sequence.
Get the number of branches (0 for linear sequences).
Create a branching sequence with parallel execution.
Check if a sequence has parallel branches.
Get the total number of commands in the sequence.
Filter commands in the sequence, preserving structure.
Create a linear (non-branching) sequence.
Check if a sequence is linear (no parallel branches).
Generate all possible linearizations of a branching sequence.
Map a function over all commands in the sequence, preserving structure.
Prepend a command to the beginning of the sequence.
Convert a sequence to a flat list of commands.
Types
Functions
Add a new branch to the sequence.
If the sequence is linear, converts it to branching with the existing commands as prefix and the new branch.
Append a command to the end of the sequence.
For linear sequences, appends to prefix. For branching sequences, appends to suffix.
@spec branch_count(t()) :: non_neg_integer()
Get the number of branches (0 for linear sequences).
Create a branching sequence with parallel execution.
Parameters
prefix- Commands to execute before branchingbranches- List of branches, each branch is a list of commandssuffix- Commands to execute after branches merge (optional)
Examples
iex> seq = PropertyDamage.Sequence.branching(
...> [setup_cmd],
...> [[branch_a_cmd1, branch_a_cmd2], [branch_b_cmd1]],
...> [cleanup_cmd]
...> )
iex> length(seq.branches)
2
Check if a sequence has parallel branches.
@spec command_count(t()) :: non_neg_integer()
Get the total number of commands in the sequence.
Counts commands in prefix + all branches + suffix.
Examples
iex> seq = PropertyDamage.Sequence.branching([cmd1], [[cmd2, cmd3], [cmd4]], [cmd5])
iex> PropertyDamage.Sequence.command_count(seq)
5
Filter commands in the sequence, preserving structure.
Empty branches are removed. If all branches become empty, converts to linear.
Create a linear (non-branching) sequence.
All commands are placed in the prefix with no branches.
Examples
iex> seq = PropertyDamage.Sequence.linear([cmd1, cmd2, cmd3])
iex> seq.prefix
[cmd1, cmd2, cmd3]
iex> seq.branches
nil
Check if a sequence is linear (no parallel branches).
Examples
iex> PropertyDamage.Sequence.linear?(%Sequence{branches: nil})
true
iex> PropertyDamage.Sequence.linear?(%Sequence{branches: [[cmd1], [cmd2]]})
false
Generate all possible linearizations of a branching sequence.
For a sequence with N branches, generates all valid interleaving orderings of the branch commands, preserving the order within each branch.
Returns a list of linear sequences, each representing one possible sequential execution order.
For linear sequences, returns a list containing just that sequence.
Complexity
For K branches with N total commands, worst case is O(N! / (n1! n2! ... * nk!)) where ni is the length of branch i.
Map a function over all commands in the sequence, preserving structure.
Examples
iex> seq = PropertyDamage.Sequence.linear([1, 2, 3])
iex> PropertyDamage.Sequence.map(seq, &(&1 * 2))
%PropertyDamage.Sequence{prefix: [2, 4, 6], branches: nil, suffix: []}
Prepend a command to the beginning of the sequence.
Convert a sequence to a flat list of commands.
For linear sequences, returns the prefix. For branching sequences, returns prefix ++ (flattened branches) ++ suffix.
Note: This loses the parallel structure - use only for display/debugging.
Examples
iex> seq = PropertyDamage.Sequence.branching([cmd1], [[cmd2], [cmd3]], [cmd4])
iex> PropertyDamage.Sequence.to_list(seq)
[cmd1, cmd2, cmd3, cmd4]