Documentation
¶
Overview ¶
Package core provides the core gear for specification-driven message processing. These specifications are structured as state transitions rules based on pattern matching against either a pending message or the state's current Bindings.
The primary type is Spec(ification), and the primary method is Walk(). A Spec specifies how to transition from one State to another State. A State is a node name (a string) and a set of Bindings (a map[string]interface{}).
A Spec can use arbitrary code for actions (and guards). When a Spec is Compiled, the compiler looks for ActionSources, each of which should specify an Interpreter. An Interpreter should know how to Compile and Exec an ActionSource. Alternately, a native Spec can provide an ActionFunc implemented in Go.
Ideally an Action does not block or perform any IO. Instead, an Action returns a structure that includes updated Bindings and zero or more messages to emit. (This structure can also include tracing and diagnostic messages.) Therefore, an action should have no side effects.
In order for an action to influence the world in some way, the package user must do something with messages that actions emit. For example, an application could provide a mechanism for certain messages to result in HTTP requests. (Such an HTTP request would then result in the subsequent response (or error) to be forwarded as a message for further processing.)
To use this package, make a Spec. Then Compile() it. You might also want to Analyze() it. Then, given an initial State an a sequence of messages, you can Walk() to the next State.
See https://github.com/Comcast/sheens for an overview.
Example ¶
Example demonstrates Walk()ing.
type note string spec := &Spec{ Name: "test", PatternSyntax: "json", Nodes: map[string]*Node{ "start": { Branches: &Branches{ Type: "message", Branches: []*Branch{ { Pattern: `{"request":"?something"}`, Target: "obey", }, { Pattern: `{"gimme":"?something"}`, Target: "ignore", }, }, }, }, "obey": { Action: &FuncAction{ F: func(ctx context.Context, bs Bindings, props StepProps) (*Execution, error) { e := NewExecution(make(Bindings)) // Forget current bindings. e.Events.AddEmitted(bs["?something"]) e.Events.AddTrace(note("polite")) return e, nil }, }, Branches: &Branches{ Branches: []*Branch{ { Target: "start", }, }, }, }, "ignore": { Action: &FuncAction{ F: func(ctx context.Context, bs Bindings, props StepProps) (*Execution, error) { e := NewExecution(make(Bindings)) // Forget current bindings. e.Events.AddTrace(note("rude")) return e, nil }, }, Branches: &Branches{ Branches: []*Branch{ { Target: "start", }, }, }, }, }, } ctx, cancel := context.WithCancel(context.Background()) defer cancel() if err := spec.Compile(ctx, nil, true); err != nil { panic(err) } st := &State{ NodeName: "start", Bs: make(Bindings), } ctl := &Control{ Limit: 10, } messages := []interface{}{ Dwimjs(`{"gimme":"queso"}`), Dwimjs(`{"request":"chips"}`), } walked, _ := spec.Walk(ctx, st, messages, ctl, nil) for i, stride := range walked.Strides { if stride.To != nil { fmt.Printf("%02d stride % -32s → % -32s consumed: %s\n", i, stride.From, stride.To, JS(stride.Consumed)) } else { fmt.Printf("%02d stride % -32s (no movement)\n", i, stride.From) } for _, m := range stride.Events.Emitted { fmt.Printf(" emit %s\n", JS(m)) } for _, m := range stride.Events.Traces.Messages { switch m.(type) { case note: fmt.Printf(" note %s\n", JS(m)) } } }
Output: 00 stride start/{} → ignore/{"?something":"queso"} consumed: {"gimme":"queso"} 01 stride ignore/{"?something":"queso"} → start/{} consumed: null note "rude" 02 stride start/{} → obey/{"?something":"chips"} consumed: {"request":"chips"} 03 stride obey/{"?something":"chips"} → start/{} consumed: null emit "chips" note "polite" 04 stride start/{} (no movement)
Index ¶
- Variables
- func Canonicalize(x interface{}) (interface{}, error)
- func Gensym(n int) string
- func IsBranchTargetVariable(s string) bool
- func Timestamp() string
- func Unquestion(p string) string
- type Action
- type ActionSource
- type BadBranching
- type Branch
- type Branches
- type Breakpoint
- type Context
- type Control
- type Events
- type Execution
- type FuncAction
- type Interpreter
- type Interpreters
- type InterpretersMap
- type Node
- type ParamSpec
- type Spec
- func (spec *Spec) Compile(ctx context.Context, interpreters Interpreters, force bool) error
- func (spec *Spec) Copy(version string) *Spec
- func (spec *Spec) ParsePatterns(ctx context.Context) error
- func (s *Spec) Spec() *Spec
- func (s *Spec) Step(ctx context.Context, st *State, pending interface{}, c *Control, ...) (*Stride, error)
- func (s *Spec) Walk(ctx context.Context, st *State, pendings []interface{}, c *Control, ...) (*Walked, error)
- type SpecNotCompiled
- type Specter
- type State
- type StepProps
- type StopReason
- type Stride
- type Traces
- type UncompiledAction
- type UnknownNode
- type UpdatableSpec
- type Walked
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // InterpreterNotFound occurs when you try to Compile an // ActionSource, and the required interpreter isn't in the // given map of interpreters. InterpreterNotFound = errors.New("interpreter not found") // DefaultInterpreters will be used in ActionSource.Compile if // the given nil interpreters. DefaultInterpreters = InterpretersMap{} // Exp_PermanentBindings is a switch to enable an experiment // that makes a binding key ending in "!" a permament binding // in the sense that an Action cannot remove that binding. // // The implementation has overhead for gathering the permanent // bindings before the Action execution and restoring those // bindings after the execution. // // One motivation for this feature is enabling an updated Spec // to be asked to do something along the lines of migration. // The old Spec version can be remembered as a permanent // binding. When that Spec is updated, we can take some // action. // // Another motivation is simply the persistence (so to speak) // of configuration-like bindings, which we do not want to // remove accidentally. With this experiment enabled, an // Action can call for removing all bindings, but the result // will still include the permanent bindings. Can make // writing Actions easier and safer. Exp_PermanentBindings = true )
var ( // DefaultPatternParser is used during Spec.Compile if the // given Spec has no PatternParser. // // This function is useful to allow a Spec to provide branch // patterns in whatever syntax is convenient. For example, if // a Spec is authored in YAML, patterns in JSON might be more // convenient (or easier to read) that patterns in YAML. DefaultPatternParser = func(syntax string, p interface{}) (interface{}, error) { switch syntax { case "none", "": return p, nil case "json": if js, is := p.(string); is { var x interface{} if err := json.Unmarshal([]byte(js), &x); err != nil { return nil, err } return x, nil } return p, nil default: return nil, errors.New("unsupposed pattern syntax: " + syntax) } } // DefaultBranchType is used for Branches.Type when // Branches.Type is zero. This var should probably be a // const. DefaultBranchType = "bindings" )
var ( // TracesInitialCap is the initial capacity for Traces buffers. TracesInitialCap = 16 // EmittedMessagesInitialCap is the initial capacity for // slices of emitted messages. EmittedMessagesInitialCap = 16 // DefaultControl will be used by Spec.Step (and therefore // Spec.Walk) if the given control is nil. DefaultControl = &Control{ Limit: 100, } // Exp_BranchTargetVariables is a switch that enables a branch // target to be a reference to a binding. If a branch target // is of the form "@VAR", then the current binding for VAR (if // any) is used as the branch target. If anything goes wrong, // the branch target is returned as the literal value of the // branch's Target. // // This feature should be used sparingly if at all. The // motivating use was for a Spec.Boot or a "boot" node/Action, // which could be used to state migration when a Spec is // updated. Being able to have a branch target that is passed // via bindings would make it much easier to write an Action // that can reset a machine based on the machine's previous // node. Exp_BranchTargetVariables = true )
var TooManyBindingss = errors.New("too many bindingss")
TooManyBindingss occurs when a guard returns more than one set of bindings.
Functions ¶
func Canonicalize ¶
func Canonicalize(x interface{}) (interface{}, error)
Canonicalize is ... hey, look over there!
func Gensym ¶
Gensym makes a random string of the given length.
Since we're returning a string and not (somehow a symbol), should be named something else. Using this name just brings back good memories.
func IsBranchTargetVariable ¶
func Timestamp ¶
func Timestamp() string
Timestamp returns a string representing the current time in RFC3339Nano.
func Unquestion ¶
Unquestion removes (so to speak) a leading question mark (if any).
Types ¶
type Action ¶
type Action interface { // Exec executes this action. // // Third argument is for parameters (which can be exposed in // the Action's dynamic environment). // // ToDo: Generalize to return []Bindings? Exec(context.Context, Bindings, StepProps) (*Execution, error) // Binds optionally gives the set of patterns that match // bindings that could be bound during execution. // // If not nil, the returned set can help with static and // dynamic analysis of the machine. Binds() []Bindings // Emits optionally gives the set of patterns that match // messages that could be emitted by this action. Emits() []interface{} }
Action returns Bindings based on the given (current) Bindings.
type ActionSource ¶
type ActionSource struct { Interpreter string `json:"interpreter,omitempty" yaml:",omitempty"` Source interface{} `json:"source"` Binds []Bindings `json:"binds,omitempty" yaml:",omitempty"` }
ActionSource can be compiled to an Action.
func (*ActionSource) Compile ¶
func (a *ActionSource) Compile(ctx context.Context, interpreters Interpreters) (Action, error)
Compile attempts to compile the ActionSource into an Action using the given interpreters, which defaults to DefaultInterpreters.
func (*ActionSource) Copy ¶
func (a *ActionSource) Copy() *ActionSource
Copy makes a shallow copy.
Needed for Specification.Copy().
type BadBranching ¶
BadBranching occurs when somebody the a Spec.Branches isn't right.
For example, a Branch with an action must have braching type "message". If not, you'll get an BadBranching error.
func (*BadBranching) Error ¶
func (e *BadBranching) Error() string
type Branch ¶
type Branch struct { // Pattern is matched against either a pending message or // bindings -- depending on the Branches.Type. Pattern interface{} `json:"pattern,omitempty" yaml:",omitempty"` // Guard is an optional procedure that will prevent the // transition if the procedure returns nil Bindings. Guard Action `json:"-" yaml:"-"` // GuardSource is an ActionSource that serves as a guard to // following this branch. If the guard returns nil bindings, // the branch isn't followed. Otherwise, the returns bindings // are used and the branch is followed. GuardSource *ActionSource `json:"guard,omitempty" yaml:"guard,omitempty"` // Target is the name of the next state for this transition. Target string `json:"target,omitempty" yaml:",omitempty"` }
Branch is a possible transition to the next state.
type Branches ¶
type Branches struct { // Type is either "message", "bindings", or nil. // // Type "message" means that an message is required and will be // consumed when branches are considered. Branch Patterns are // matched against that message. // // Type "bindings" means that Branch Patterns are matched // against the current Bindings. // // A nil Type should imply only one Branch with no Pattern. Type string `json:"type,omitempty" yaml:",omitempty"` // Modes is a set of flags that can inform Branch processing // and analysis. Currently no modes are considered. // // Example: "exclusive" might declare that the Branch patterns // should be mututally exclusive. // // ToDo: Use a real type instead of string. Modes []string `json:"modes,omitempty" yaml:",omitempty"` // Branches is the list (ordered) of possible transitions to // the next state (if any). // // No Branches means that this node is terminal. Branches []*Branch `json:"branches,omitempty" yaml:",omitempty"` }
Branches represents the possible transitions to next states.
type Breakpoint ¶
Breakpoint is a *State predicate.
When a Breakpoint returns true for a *State, then processing should stop at that point.
type Context ¶
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
Context is a copy of context.Context.
This type is defined here in order to avoid importing context.Context, which pulls with it a ton of other stuff (e.g. fmt). See https://github.com/Comcast/sheens/issues/13 and 14.
type Control ¶
type Control struct { // Limit is the maximum number of Steps that a Walk() can take. Limit int `json:"limit"` Breakpoints map[string]Breakpoint `json:"-"` }
Control influences how Walk() operates.
type Events ¶
type Events struct { Emitted []interface{} `json:"emitted,omitempty" yaml:",omitempty"` Traces *Traces `json:"traces,omitempty" yaml:",omitempty"` }
Events contains emitted messages and Traces.
func (*Events) AddEmitted ¶
func (es *Events) AddEmitted(x interface{})
AddEmitted adds the given thing to the list of emitted messages.
type Execution ¶
type Execution struct { Bs Bindings *Events }
func NewExecution ¶
func NewExecution(bs Bindings) *Execution
type FuncAction ¶
type FuncAction struct { F func(context.Context, Bindings, StepProps) (*Execution, error) `json:"-" yaml:"-"` // contains filtered or unexported fields }
FuncAction is currently a wrapper around a Go function, but an Action will eventually be a specification for generating an outbound message.
func (*FuncAction) Binds ¶
func (a *FuncAction) Binds() []Bindings
func (*FuncAction) Emits ¶
func (a *FuncAction) Emits() []interface{}
type Interpreter ¶
type Interpreter interface { // Compile can make something that helps when Exec()ing the // code later. Compile(ctx context.Context, code interface{}) (interface{}, error) // Exec executes the code. The result of previous Compile() // might be provided. Exec(ctx context.Context, bs Bindings, props StepProps, code interface{}, compiled interface{}) (*Execution, error) }
Interpreter can optionally compile and execute code for Actions and guards.
type Interpreters ¶
type Interpreters interface {
Find(interpreterName string) Interpreter
}
Interpreters resolves an interpreter name (like "ecmascript") to an Interpreter.
See InterpretersMap for a simple implementation.
type InterpretersMap ¶
type InterpretersMap map[string]Interpreter
InterpretersMap is a simple implementation of Interpreters.
func NewInterpretersMap ¶
func NewInterpretersMap() InterpretersMap
func (InterpretersMap) Find ¶
func (m InterpretersMap) Find(name string) Interpreter
type Node ¶
type Node struct { // Doc is optional document in a format of your choosing. Doc string `json:"doc,omitempty" yaml:",omitempty"` // Action is an optional action that will be executed upon // transition to this node. // // Note that a node with "message"-based branching cannot have // an Action. Action Action `json:"-" yaml:"-"` // ActionSource, if given, is Compile()ed to an Action. ActionSource *ActionSource `json:"action,omitempty" yaml:"action,omitempty"` // Branches contains the transitions out of this node. Branches *Branches `json:"branching,omitempty" yaml:"branching,omitempty"` }
Node represents the structure of something like a state in a state machine.
In our machines, the state is really given by (1) the name of the current node (At) and (2) the current Bindings. A Node given a optional Action and possible state transitions.
type ParamSpec ¶
type ParamSpec struct { // Doc describes the spec in English and Markdown. Audience // is developers, not users. Doc string `json:"doc,omitempty" yaml:",omitempty"` // PrimitiveType is (for now) any string. PrimitiveType string `json:"primitiveType" yaml:"primitiveType"` // Default is the default value for a parameter used in case a value is not given in the initial bindings Default interface{} `json:"default"` // Optional means that the parameter is not required! Optional bool `json:"optional,omitempty" yaml:",omitempty"` // IsArray specifies whether a value must be an array of // values of the PrimitiveType. IsArray bool `json:"isArray,omitempty" yaml:"isArray,omitempty"` // SemanticType is (for now) any string. SemanticType string `json:"semanticType,omitempty" yaml:"semanticType,omitempty"` // MinCardinality is the minimum number of values. MinCardinality int `json:"minCard,omitempty" yaml:"minCard,omitempty"` // MaxCardinality is the maximum number of values. // // A MaxCardinality greater than one implies the param value // IsArray. MaxCardinality int `json:"maxCard,omitempty" yaml:"maxCard,omitempty"` // Predicate isn't implemented. // // Could be a predicate that could further validate a // parameter value. Predicate interface{} `json:"predicate,omitempty" yaml:",omitempty"` // Advisory indicates that a violation of this spec is a // warning, not an error. Advisory bool `json:"advisory,omitempty" yaml:",omitempty"` }
ParamSpec is a strawman struct to represent data about required and optional Machine parameters (which are just initial bindings).
Probably would be better to start with an interface, but then deserialization is more trouble.
We're not over-thinking this struct right now. Really just a strawman. Should be much better.
func (*ParamSpec) Valid ¶
Valid should return an error if the given spec is bad for some reason.
Currently just returns nil.
Probably shouldn't return an error, but we'll just go with that for now.
func (*ParamSpec) ValueCompilesWith ¶
ValueCompilesWith checks that the given value complies with the spec. Returns an error if not.
Currently just returns nil.
Probably shouldn't return an error, but we'll just go with that for now.
type Spec ¶
type Spec struct { // Name is the generic name for this machine. Something like // "door-open-notification". Cf. Id. Name string `json:"name,omitempty" yaml:",omitempty"` // Version is the version of this generic machine. Something // like "1.2". Version string `json:"version,omitempty" yaml:",omitempty"` // Id should be a globally unique identifier (such as a hash // of a canonical representation of the Spec). // // This value could be used to determine when a Spec has // changed. // // This package does not read or write this value. Id string `json:"id,omitempty" yaml:",omitempty"` // Doc is general documentation about how this specification works. Doc string `json:"doc,omitempty" yaml:",omitempty"` // ParamSpecs is an optional name from a parameter name to a // specification for that parameter. // // A parameter is really just an initial binding that's // provided when a machine is created. // // ToDo: Implement actual check of parameters when machine is // created. ParamSpecs map[string]ParamSpec `json:"paramSpecs,omitempty" yaml:",omitempty"` // Uses is a set of feature tags. Uses []string `json:"uses,omitempty" yaml:",omitempty"` // Nodes is the structure of the machine. This value could be // a reference that points into a library or whatever. Nodes map[string]*Node `json:"nodes,omitempty" yaml:",omitempty"` // ErrorNode is an optional name of a node for the machine in // the even of an internal error. // // Probably should just always assume the convention that a // node named 'error' is the error node. ToDo: Consider. ErrorNode string `json:"errorNode,omitempty" yaml:",omitempty"` // NoAutoErrorNode will instruct the spec compiler not to add // an error node if one does not already exist. NoAutoErrorNode bool `json:"noErrorNode,omitempty" yaml:",omitempty"` // ActionErrorBranches (when true) means that this spec uses // branches to handle action errors. (A branch can match an // action error using a "actionError" property with a variable // value.) // // If this switch is off, then any action error will result in // a transition to the error state, which is probably not what // you want. ActionErrorBranches bool `json:"actionErrorBranches,omitempty" yaml:",omitempty"` // ActionErrorNode is the name of the target node when an // action produces an error. // // If no value is given, then Step() will return an error // rather than a stride ending at a node given by this value. ActionErrorNode string `json:"actionErrorNode,omitempty" yaml:",omitempty"` // Boot is an optional Action that should be (?) executed when // the machine is loaded. Not implemented yet. Boot Action `json:"-" yaml:"-"` // BootSource, if given, can be compiled to a Boot Action. // See Spec.Compile. BootSource *ActionSource `json:"boot,omitempty" yaml:"boot,omitempty"` // Toob is of course Boot in reverse. It's also an optional // Action that can/should be executed when a Machine is // unloaded, suspended, or whatever. Not currently connected // to anything. Toob Action `json:"-" yaml:"-"` // ToobSource, if given, can be compiled to a Toob Action. // See Spec.Compile. ToobSource *ActionSource `json:"toob,omitempty" yaml:"toob,omitempty"` // PatternSyntax indicates the syntax (if any) for branch patterns. PatternSyntax string `json:"patternSyntax,omitempty" yaml:",omitempty"` PatternParser func(string, interface{}) (interface{}, error) `json:"-" yaml:"-"` // NoNewMachines will make Step return an error if a pattern // match returns more than one set of bindings. // // ToDo: Implement. NoNewMachines bool `json:"noNewMachined,omitempty" yaml:",omitempty"` // contains filtered or unexported fields }
Spec is a specification used to build a machine.
A specification gives the structure of the machine. This data does not include any state (such as the name of the current Node or a Machine's Bindings).
If a specification includes Nodes with ActionSources, then the specification should be Compiled before use.
func TurnstileSpec ¶
TurnstileSpec makes an example Spec that's useful to have around.
See https://en.wikipedia.org/wiki/Finite-state_machine#Example:_coin-operated_turnstile.
func (*Spec) Compile ¶
Compile compiles all action-like sources into actions. Might also do some other things.
Action-like sources include Actions, Boot, Toob, and Guards.
func (*Spec) ParsePatterns ¶
ParsePatterns parses branch patterns.
The method Compile calls this method. ParsePatterns is exposed to tools that might need to parse patterns without wanted to Compile them.
func (*Spec) Step ¶
func (s *Spec) Step(ctx context.Context, st *State, pending interface{}, c *Control, props StepProps) (*Stride, error)
Step is the fundamental operation that attempts to move from the given state.
The given pending message (if any) will be consumed by "message" type Branches.
func (*Spec) Walk ¶
func (s *Spec) Walk(ctx context.Context, st *State, pendings []interface{}, c *Control, props StepProps) (*Walked, error)
Walk takes as many steps as it can.
Any returned error is an internal error. Almost all errors encountered during processing should transition to the "error" node with a binding for "error".
Any unprocessed messages are returned. This method should only returned some unprocessed messages if the method encountered an internal error.
type SpecNotCompiled ¶
type SpecNotCompiled struct {
Spec *Spec
}
SpecNotCompiled occurs when a Spec is used (say via Step()) before it has been Compile()ed.
func (*SpecNotCompiled) Error ¶
func (e *SpecNotCompiled) Error() string
type Specter ¶
type Specter interface {
Spec() *Spec
}
Specter enables other things to manifest themselves as Specs.
Specters can be spooky.
A Spec is itself a Specter. An UpdatableSpec is also a Specter, but it's not itself Spec.
Specter is not used anywhere in this package. It's defined here for convenience and encouragement.
type State ¶
type State struct { NodeName string `json:"node"` Bs Bindings `json:"bs"` }
State represents the current state of a machine given a specification.
type StopReason ¶
type StopReason int
StopReason represents the possible reasons for a Walk to terminate.
const ( Done StopReason = iota // Went as far as the Spec allowed. Limited // Too many steps. InternalError // What else to do? BreakpointReached // During a Walk. )
func (StopReason) MarshalJSON ¶
func (r StopReason) MarshalJSON() ([]byte, error)
MarshalJSON is generated so StopReason satisfies json.Marshaler.
func (StopReason) String ¶
func (i StopReason) String() string
func (*StopReason) UnmarshalJSON ¶
func (r *StopReason) UnmarshalJSON(data []byte) error
UnmarshalJSON is generated so StopReason satisfies json.Unmarshaler.
type Stride ¶
type Stride struct { // Events gather what was emitted during the step. *Events `json:"events,omitempty" yaml:",omitempty"` // From is the name of the starting node. From *State `json:"from,omitempty" yaml:",omitempty"` // To is the new State (if any) resulting from the step. To *State `json:"to,omitempty" yaml:",omitempty"` // Consumed is the message (if any) that was consumed by the step. Consumed interface{} `json:"consumed,omitempty" yaml:",omitempty"` }
Stride represents a step that Walk has taken or attempted.
type Traces ¶
type Traces struct {
Messages []interface{} `json:"messages,omitempty" yaml:",omitempty"`
}
Traces holds trace messages.
type UncompiledAction ¶
UncompiledAction occurs when an ActionSource execution is attempted but that ActionSource hasn't been Compile()ed. Usually, this compilation happens as part of Spec.Compile().
func (*UncompiledAction) Error ¶
func (e *UncompiledAction) Error() string
type UnknownNode ¶
UnknownNode occurs when a branch is followed and its target node is not in the Spec.
func (*UnknownNode) Error ¶
func (e *UnknownNode) Error() string
type UpdatableSpec ¶
type UpdatableSpec struct {
// contains filtered or unexported fields
}
UpdatableSpec is a scary yet handy Specter with an underlying Spec that can be changed at any time.
This capability motivated Specters.
func NewUpdatableSpec ¶
func NewUpdatableSpec(spec *Spec) *UpdatableSpec
NewUpdatableSpec makes one with the given initial spec, which can be changed later via SetSpec.
func (*UpdatableSpec) SetSpec ¶
func (s *UpdatableSpec) SetSpec(spec *Spec) error
SetSpec atomically changes the underlying spec.
func (*UpdatableSpec) Spec ¶
func (s *UpdatableSpec) Spec() *Spec
Spec implements the Specter interface.
type Walked ¶
type Walked struct { // Strides contains each Stride taken and the last one // attempted. Strides []*Stride `json:"strides" yaml:",omitempty"` // Remaining stores the messages that Walk failed to consume. Remaining []interface{} `json:"remaining,omitempty" yaml:",omitempty"` // StoppedBecause reports the reason why the Walk stopped. StoppedBecause StopReason `json:"stoppedBecause,omitempty" yaml:",omitempty"` // Error stores an internal error that occurred (if any). Error error `json:"error,omitempty" yaml:",omitempty"` // BreakpointId is the id of the breakpoint, if any, that // caused this Walk to stop. BreakpointId string `json:"breakpoint,omitempty" yaml:",omitempty"` }
Walked represents a sequence of strides taken by a Walk().