{"id":13677452,"url":"https://github.com/Gurpartap/statemachine-go","last_synced_at":"2025-04-29T11:30:55.813Z","repository":{"id":63014744,"uuid":"101462804","full_name":"Gurpartap/statemachine-go","owner":"Gurpartap","description":"🚦 Declarative Finite-State Machines in Go","archived":false,"fork":false,"pushed_at":"2023-08-09T21:42:33.000Z","size":110,"stargazers_count":97,"open_issues_count":1,"forks_count":17,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-08-02T13:17:59.893Z","etag":null,"topics":["decision","decision-trees","declarative-routing","deterministic-finite-automata","finite-state-machine","fsm","go","golang","state","state-machine","statecharts","statemachine","workflow"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Gurpartap.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":"License.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-08-26T04:39:15.000Z","updated_at":"2024-07-11T02:41:33.000Z","dependencies_parsed_at":"2024-01-07T01:19:51.628Z","dependency_job_id":null,"html_url":"https://github.com/Gurpartap/statemachine-go","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gurpartap%2Fstatemachine-go","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gurpartap%2Fstatemachine-go/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gurpartap%2Fstatemachine-go/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Gurpartap%2Fstatemachine-go/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Gurpartap","download_url":"https://codeload.github.com/Gurpartap/statemachine-go/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224163579,"owners_count":17266530,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["decision","decision-trees","declarative-routing","deterministic-finite-automata","finite-state-machine","fsm","go","golang","state","state-machine","statecharts","statemachine","workflow"],"created_at":"2024-08-02T13:00:42.447Z","updated_at":"2024-11-11T19:31:48.888Z","avatar_url":"https://github.com/Gurpartap.png","language":"Go","funding_links":[],"categories":["State Machines","Go","Libraries"],"sub_categories":["Go"],"readme":"\u003cimg alt=\"StateMachine\" src=\"https://user-images.githubusercontent.com/39792/29720866-d95eda6a-89d8-11e7-907e-6f0edd7f4ee3.png\" width=\"538\" height=\"72\"/\u003e\n\nStateMachine supports creating productive State Machines In Go\n\n[![GoDoc](https://godoc.org/github.com/Gurpartap/statemachine-go?status.svg)](https://godoc.org/github.com/Gurpartap/statemachine-go)\n\n\u003c!-- TOC --\u003e\n\n- [Introduction](#introduction)\n    - [Further Reading](#further-reading)\n    - [Installation](#installation)\n- [Usage](#usage)\n    - [Project Goals](#project-goals)\n    - [States and Initial State](#states-and-initial-state)\n    - [Events](#events)\n\t- [Timed Events](#timed-events)\n\t- [Choice](#choice)\n    - [Transitions](#transitions)\n    - [Transition Guards (Conditions)](#transition-guards-conditions)\n    - [Transition Callbacks](#transition-callbacks)\n        - [Before Transition](#before-transition)\n        - [Around Transition](#around-transition)\n        - [After Transition](#after-transition)\n\t- [Event Callbacks](#event-callbacks)\n        - [After Failure](#after-failure)\n    - [Matchers](#matchers)\n        - [Event Transition Matchers](#event-transition-matchers)\n        - [Transition Callback Matchers](#transition-callback-matchers)\n        - [Event Callback Matchers](#event-callback-matchers)\n        - [Callback Functions](#callback-functions)\n- [About](#about)\n\n\u003c!-- /TOC --\u003e\n\n## Introduction\n\n[`TLDR Turnstile Example`](https://github.com/Gurpartap/statemachine-go/blob/master/examples/turnstile/main.go)\n\n\u003ca href=\"https://raw.githubusercontent.com/Gurpartap/statemachine-go/master/examples/turnstile/diagram.svg?sanitize=true\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/Gurpartap/statemachine-go/master/examples/turnstile/diagram.svg?sanitize=true\" alt=\"Turnstile StateMachine Example Diagram\" /\u003e\u003c/a\u003e\n\nState machines provide an alternative way of thinking about how we code any\njob/process/workflow.\n\nUsing a state machine for an object, that reacts to events differently based on\nits current state, reduces the amount of boilerplate and duct-taping you have\nto introduce to your code.\n\nStateMachine package provides a feature complete implementation of\nfinite-state machines in Go.\n\n__What Is A Finite-State Machine Even?__\n\n\u003e A finite-state machine (FSM) is an abstract machine that can be in exactly one\n\u003e of a finite number of states at any given time. The FSM can change from one\n\u003e state to another in response to some external inputs; the change from one\n\u003e state to another is called a transition. An FSM is defined by a list of its\n\u003e states, its initial state, and the conditions for each transition.\n\u003e\n\u003e — [Wikipedia](https://en.wikipedia.org/wiki/Finite-state_machine)\n\n### Further Reading\n\n- [Finite-state Machine](https://en.wikipedia.org/wiki/Finite-state_machine) on Wikipedia\n- [Coding State Machines in C or C++](https://barrgroup.com/Embedded-Systems/How-To/Coding-State-Machines) by Miro Samek\n- [Statecharts](https://statecharts.github.io)\n- [state_machines Ruby Gem](https://github.com/state-machines/state_machines)\n- [State Charts XML Notation](https://www.w3.org/TR/scxml/)\n- [Flying Spaghetti Monster](https://en.wikipedia.org/wiki/Flying_Spaghetti_Monster)\n\n### Installation\n\nRun this in your project directory:\n\n```bash\ngo get -u https://github.com/Gurpartap/statemachine-go\n```\n\nImport StateMachine with this line in your Go code:\n\n```go\nimport \"github.com/Gurpartap/statemachine-go\"\n```\n\n## Usage\n\n### Project Goals\n\n\u003e A complex system that works is invariably found to have evolved from a simple\n\u003e system that worked. A complex system designed from scratch never works and\n\u003e cannot be patched up to make it work. You have to start over with a working\n\u003e simple system.\n\u003e\n\u003e – [John Gall (1975)](https://en.wikipedia.org/wiki/John_Gall_(author)#Gall.27s_law)\n\nStateMachine is simple in its specification, DSL, and internal implementation.\nAnd it works. There are no plans to introduce advanced FSM features such as\nregions, submachines, history based transitions, join, fork, etc., unless\nthere's a simple way to do so without affecting the rest of the implementation.\nWell, submachines have already been implemented (partially and is in flux).\n\nPerformance is generally a significant factor when considering the use of a\nthird party package. However, an API that I can actually code and design in my\nmind, ahead of using it, is just as important to me.\n\nStateMachine's API design and developer productivity take precedence over\nits benchmark numbers (especially when compared to a bare metal switch\nstatement based state machine implementation, which may not take you as far).\n\nFor this, StateMachine provides a DSL using its builder objects. These\nbuilders compute and validate the state definitions, and then inject the\nresult (states, events, transitions, callbacks, etc.) into the state machine\nduring its initialization. Subsequently, these builders are free to be\ngarbage collected.\n\nMoreover, the state _machinery_ is not dependent on these DSL builders. State\nmachines may also be initialized from directly allocating definition structs,\nor even parsing them from JSON, along with pre-registered callback references.\n\nStateMachine definitions comprise of the following basic components:\n\n- [States and Initial State](#states-and-initial-state)\n- [Events](#events)\n- [Transitions](#transitions)\n- [Transition Guards](#transition-guards-conditions)\n- [Transition Callbacks](#transition-callbacks)\n\nThese, and some additional components are covered below, along with their\nexample usage code.\n\nAdding a state machine is as simple as embedding statemachine.Machine in\nyour struct, defining states and events, along with their transitions.\n\n```go\ntype Process struct {\n    statemachine.Machine\n\n    // or\n\n    Machine statemachine.Machine\n}\n\nfunc NewProcess() *Process {\n    process := \u0026Process{}\n\n    process.Machine = statemachine.NewMachine()\n    process.Machine.Build(func(m statemachine.MachineBuilder) {\n        // ...\n    })\n\n    // or\n\n    process.Machine = statemachine.BuildNewMachine(func(m statemachine.MachineBuilder) {\n        // ...\n    })\n\n    return process\n}\n```\n\nStates, events, and transitions are defined using a DSL composed of \"builders\",\nincluding `statemachine.MachineBuilder` and\n`statemachine.EventBuilder`. These builders provide a clean and type-safe DSL\nfor writing the specification of how the state machine functions.\n\nThe subsequent examples are a close port of\n[my experience](https://github.com/Gurpartap/cognizant/blob/master/lib/cognizant/process.rb#L28-L87)\nwith using the [state_machines](https://github.com/state-machines/state_machines)\nRuby gem, from which StateMachine Go package's DSL is highly inspired.\n\n\u003ca href=\"https://raw.githubusercontent.com/Gurpartap/statemachine-go/master/examples/cognizant/diagram.svg?sanitize=true\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/Gurpartap/statemachine-go/master/examples/cognizant/diagram.svg?sanitize=true\" alt=\"System Process StateMachine Example Diagram\" /\u003e\u003c/a\u003e\n\nThe example represented in the diagram above is implemented in [`examples/cognizant/process.go`](https://github.com/Gurpartap/statemachine-go/blob/master/examples/cognizant/process.go).\n\nIf, instead of the builders DSL, you would rather want to specify the\nStateMachine directly using definition structs, take a look at the\n[ExampleMachineDef](https://github.com/Gurpartap/statemachine-go/blob/master/machine_builder_test.go#L51-L91)\ntest function. The same may also be imported from JSON or\n[HCL](https://github.com/Gurpartap/statemachine-go/tree/master/examples/hcl).\n\n### States and Initial State\n\nPossible states in the state machine may be manually defined, along with the\ninitial state. However, states are also inferred from event transition\ndefinitions.\n\nInitial state is set during the initialization of the state machine, and is\nrequired to be defined in the builder.\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    m.States(\"unmonitored\", \"running\", \"stopped\")\n    m.States(\"starting\", \"stopping\", \"restarting\")\n\n    // Initial state must be defined.\n    m.InitialState(\"unmonitored\")\n})\n```\n\n### Events\n\nEvents act as a virtual function which when fired, trigger a state transition.\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    m.Event(\"monitor\", ... )\n\n    m.Event(\"start\", ... )\n\n    m.Event(\"stop\", ... )\n\n    m.Event(\"restart\", ... )\n\n    m.Event(\"unmonitor\", ... )\n\n    m.Event(\"tick\", ... )\n})\n```\n\n### Timed Events\n\nCurrently there one one timed event available:\n\n#### `TimedEvery(duration time.Duration)`\n\nMakes the event fire automatically at every specified duration.\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n\tm.Event(\"tick\", func(e statemachine.EventBuilder) {\n\t\te.TimedEvery(1 * time.Second)\n\t\t// e.Transition().From(...).To(...)\n\t}\n\n\t// or\n\n\tm.Event(\"tick\").\n\t\tTimedEvery(1 * time.Second).\n\t\t// e.Transition().From(...).To(...)\n}\n```\n\n### Choice\n\nChoice assists in choosing event transition(s) based on a boolean condition.\n\nNote that Choice is not executed if the event also specifies transitions of its\nown.\n\nThe example below runs the `tick` event every second, and decides the state to\ntransition to based on based on whether the process is currently running on the\nsystem or not, as long as we're also not set to `SkipTicks` (for\nstart, stop, and restart grace times).\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n\t// the nested way:\n\n\tm.Event(\"tick\", func(e statemachine.EventBuilder) {\n\t\te.Choice(\u0026process.IsProcessRunning, func(c statemachine.ChoiceBuilder) {\n\t\t\tc.Unless(process.SkipTick)\n\n\t\t\tc.OnTrue(func(e statemachine.EventBuilder) {\n\t\t\t\t// e.Transition().From(...).To(...)\n\t\t\t})\n\n\t\t\tc.OnFalse(func(e statemachine.EventBuilder) {\n\t\t\t\t// e.Transition().From(...).To(...)\n\t\t\t})\n\t\t})\n\t})\n\n\t// preferred alternative syntax:\n\n\tm.Event(\"tick\").\n\t\tTimedEvery(1 * time.Second).\n\t\tChoice(\u0026process.IsProcessRunning).Label(\"isRunning\"). // Label helps with diagrams and debugging\n\t\tUnless(process.SkipTick). // TODO: move this to SkipUntil\n\t\tOnTrue(func(e statemachine.EventBuilder) {\n\t\t\te.Transition().From(\"starting\").To(\"running\")\n\t\t\te.Transition().From(\"restarting\").To(\"running\")\n\t\t\te.Transition().From(\"stopping\").To(\"running\")\n\t\t\te.Transition().From(\"stopped\").To(\"running\")\n\t\t}).\n\t\tOnFalse(func(e statemachine.EventBuilder) {\n\t\t\te.Transition().From(\"starting\").To(\"stopped\")\n\t\t\te.Transition().From(\"restarting\").To(\"stopped\")\n\t\t\te.Transition().From(\"running\").To(\"stopped\")\n\t\t\te.Transition().From(\"stopping\").To(\"stopped\")\n\t\t\te.Transition().From(\"stopped\").To(\"starting\").\n\t\t\t\tIf(\u0026process.ShouldAutoStart).Label(\"shouldAutoStart\")\n\t\t\t})\n}\n```\n\n### Transitions\n\nTransitions represent the change in state when an event is fired.\n\nNote that `.From(states ...string)` accepts variadic args.\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    m.Event(\"monitor\", func(e statemachine.EventBuilder) {\n        e.Transition().From(\"unmonitored\").To(\"stopped\")\n    })\n\n    m.Event(\"start\", func(e statemachine.EventBuilder) {\n        // from either of the defined states\n        e.Transition().From(\"unmonitored\", \"stopped\").To(\"starting\")\n    })\n\n    m.Event(\"stop\", func(e statemachine.EventBuilder) {\n        e.Transition().From(\"running\").To(\"stopping\")\n    })\n\n    m.Event(\"restart\", func(e statemachine.EventBuilder) {\n        e.Transition().From(\"running\", \"stopped\").To(\"restarting\")\n    })\n\n    m.Event(\"unmonitor\", func(e statemachine.EventBuilder) {\n        e.Transition().FromAny().To(\"unmonitored\")\n    })\n\n    m.Event(\"tick\", func(e statemachine.EventBuilder) {\n        // ...\n    })\n})\n```\n\n### Transition Guards (Conditions)\n\nTransition Guards are conditional callbacks which expect a boolean return\nvalue, implying whether or not the transition in context should occur.\n\n```go\ntype TransitionGuardFnBuilder interface {\n    If(guardFunc ...TransitionGuardFunc)\n    Unless(guardFunc ...TransitionGuardFunc)\n}\n```\n\nValid TransitionGuardFunc signatures:\n\n```go\n*bool\nfunc() bool\nfunc(transition statemachine.Transition) bool\n```\n\n```go\n// Assuming process.IsProcessRunning is a bool variable, and\n// process.GetIsProcessRunning is a func returning a bool value.\nm.Event(\"tick\", func(e statemachine.EventBuilder) {\n    // If guard\n    e.Transition().From(\"starting\").To(\"running\").If(\u0026process.IsProcessRunning)\n\n    // Unless guard\n    e.Transition().From(\"starting\").To(\"stopped\").Unless(process.GetIsProcessRunning)\n\n    // ...\n\n    e.Transition().From(\"stopped\").To(\"starting\").If(func(t statemachine.Transition) bool {\n        return process.ShouldAutoStart \u0026\u0026 !process.GetIsProcessRunning()\n    })\n\n    // or\n\n    e.Transition().From(\"stopped\").To(\"starting\").\n        If(\u0026process.ShouldAutoStart).\n        AndUnless(\u0026process.IsProcessRunning)\n\n    // ...\n})\n```\n\n### Transition Callbacks\n\nTransition Callback methods are called before, around, or after a transition.\n\n- [Before Transition](#before-transition)\n- [Around Transition](#around-transition)\n- [After Transition](#after-transition)\n\n#### Before Transition\n\n`Before Transition` callbacks do not act as a conditional, and a bool return\nvalue will not impact the transition.\n\nValid TransitionCallbackFunc signatures:\n\n```go\nfunc()\nfunc(m statemachine.Machine)\nfunc(t statemachine.Transition)\nfunc(m statemachine.Machine, t statemachine.Transition)\n```\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    // ...\n\n    m.BeforeTransition().FromAny().To(\"stopping\").Do(func() {\n        process.ShouldAutoStart = false\n    })\n\n    // ...\n}\n```\n\n#### Around Transition\n\n`Around Transition`'s callback provides a next func as input, which must be\ncalled inside the callback. (TODO: Missing to call the method will trigger a runtime\nfailure with an appropriately described error.)\n\nValid TransitionCallbackFunc signatures:\n\n```go\nfunc(next func())\nfunc(m statemachine.Machine, next func())\nfunc(t statemachine.Transition, next func())\nfunc(m statemachine.Machine, t statemachine.Transition, next func())\n```\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    // ...\n\n    m.\n        AroundTransition().\n        From(\"starting\", \"restarting\").\n        To(\"running\").\n        Do(func(next func()) {\n            start := time.Now()\n\n            // it'll trigger a failure if next is not called\n            next()\n\n            elapsed = time.Since(start)\n\n            log.Printf(\"it took %s to [re]start the process.\\n\", elapsed)\n        })\n\n    // ...\n})\n```\n\n#### After Transition\n\n`After Transition` callback is called when the state has successfully\ntransitioned.\n\nValid TransitionCallbackFunc signatures:\n\n```go\nfunc()\nfunc(m statemachine.Machine)\nfunc(t statemachine.Transition)\nfunc(m statemachine.Machine, t statemachine.Transition)\n```\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    // ...\n\n    // notify system admin\n    m.AfterTransition().From(\"running\").ToAny().Do(process.DialHome)\n\n    // log all transitions\n    m.\n        AfterTransition().\n        Any().\n        Do(func(t statemachine.Transition) {\n            log.Printf(\"State changed from '%s' to '%s'.\\n\", t.From(), t.To())\n        })\n\n    // ...\n})\n```\n\n### Event Callbacks\n\nThere is only one Event Callback method, which is called after an event fails\nto transition the state.\n\n- [After Failure](#after-failure)\n\n#### After Failure\n\n`After Failure` callback is called when there's an error with event firing.\n\nValid TransitionCallbackFunc signatures:\n\n```go\nfunc()\nfunc(err error)\nfunc(m statemachine.Machine, err error)\nfunc(t statemachine.Event, err error)\nfunc(m statemachine.Machine, t statemachine.Event, err error)\n```\n\n```go\nprocess.Machine.Build(func(m statemachine.MachineBuilder) {\n    // ...\n\n    m.AfterFailure().OnAnyEvent().\n        Do(func(e statemachine.Event, err error) {\n            log.Printf(\n                \"could not transition with event='%s' err=%+v\\n\",\n                e.Event(),\n                err\n            )\n        })\n\n    // ...\n})\n```\n\n### Matchers\n\n#### Event Transition Matchers\n\nThese may map from one or more `from` states to exactly one `to` state.\n\n```go\ntype TransitionBuilder interface {\n    From(states ...string) TransitionFromBuilder\n    FromAny() TransitionFromBuilder\n    FromAnyExcept(states ...string) TransitionFromBuilder\n}\n\ntype TransitionFromBuilder interface {\n    ExceptFrom(states ...string) TransitionExceptFromBuilder\n    To(state string) TransitionToBuilder\n}\n\ntype TransitionExceptFromBuilder interface {\n    To(state string) TransitionToBuilder\n}\n```\n\n__Examples:__\n\n```go\ne.Transition().From(\"first_gear\").To(\"second_gear\")\n\ne.Transition().From(\"first_gear\", \"second_gear\", \"third_gear\").To(\"stalled\")\n\nallGears := vehicle.GetAllGearStates()\ne.Transition().From(allGears...).ExceptFrom(\"neutral_gear\").To(\"stalled\")\n\ne.Transition().FromAny().To(\"stalled\")\n\ne.Transition().FromAnyExcept(\"neutral_gear\").To(\"stalled\")\n```\n\n#### Transition Callback Matchers\n\nThese may map from one or more `from` states to one or more `to` states.\n\n```go\ntype TransitionCallbackBuilder interface {\n    From(states ...string) TransitionCallbackFromBuilder\n    FromAny() TransitionCallbackFromBuilder\n    FromAnyExcept(states ...string) TransitionCallbackFromBuilder\n}\n\ntype TransitionCallbackFromBuilder interface {\n    ExceptFrom(states ...string) TransitionCallbackExceptFromBuilder\n    To(states ...string) TransitionCallbackToBuilder\n    ToSame() TransitionCallbackToBuilder\n    ToAny() TransitionCallbackToBuilder\n    ToAnyExcept(states ...string) TransitionCallbackToBuilder\n}\n\ntype TransitionCallbackExceptFromBuilder interface {\n    To(states ...string) TransitionCallbackToBuilder\n    ToSame() TransitionCallbackToBuilder\n    ToAny() TransitionCallbackToBuilder\n    ToAnyExcept(states ...string) TransitionCallbackToBuilder\n}\n```\n\n__Examples:__\n\n```go\nm.BeforeTransition().From(\"idle\").ToAny().Do(someFunc)\n\nm.AroundTransition().From(\"state_x\").ToAnyExcept(\"state_y\").Do(someFunc)\n\nm.AfterTransition().Any().Do(someFunc)\n// ...is same as:\nm.AfterTransition().FromAny().ToAny().Do(someFunc)\n```\n\n#### Event Callback Matchers\n\nThese may match on one or more `events`.\n\n```go\ntype EventCallbackBuilder interface {\n\tOn(events ...string) EventCallbackOnBuilder\n\tOnAnyEvent() EventCallbackOnBuilder\n\tOnAnyEventExcept(events ...string) EventCallbackOnBuilder\n}\n\ntype EventCallbackOnBuilder interface {\n\tDo(callbackFunc EventCallbackFunc) EventCallbackOnBuilder\n}\n```\n\n__Examples:__\n\n```go\nm.AfterFailure().OnAnyEventExcept(\"event_z\").Do(someFunc)\n```\n\n#### Callback Functions\n\nAny callback function's arguments (and return types) are dynamically set based\non what types are defined (dependency injection). Setting any unavailable arg\nor return type will cause a panic during initialization.\n\nFor example, if your BeforeTransition() callback does not need access to the\n`statemachine.Transition` variable, you may just define the callback with a\nblank function signature: `func()`, instead of\n`func(t statemachine.Transition)`. Similarly, for an AfterFailure()\ncallback you can use `func(err error)`, or\n`func(e statemachine.Event, err error)`, or even just `func()` .\n\n## About\n\n    Copyright 2017 Gurpartap Singh\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGurpartap%2Fstatemachine-go","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGurpartap%2Fstatemachine-go","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGurpartap%2Fstatemachine-go/lists"}