{"id":20331148,"url":"https://github.com/comcast/sheens","last_synced_at":"2025-08-19T22:32:27.741Z","repository":{"id":27554180,"uuid":"114133179","full_name":"Comcast/sheens","owner":"Comcast","description":"Message Machines","archived":false,"fork":false,"pushed_at":"2024-04-19T12:38:59.000Z","size":2725,"stargazers_count":118,"open_issues_count":25,"forks_count":29,"subscribers_count":24,"default_branch":"main","last_synced_at":"2024-12-09T03:12:08.395Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/Comcast.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-12-13T14:49:52.000Z","updated_at":"2024-11-28T16:33:40.000Z","dependencies_parsed_at":"2024-06-18T16:43:08.083Z","dependency_job_id":"e5afadaa-20c8-43b2-9ea9-e74e806eb4e0","html_url":"https://github.com/Comcast/sheens","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Comcast%2Fsheens","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Comcast%2Fsheens/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Comcast%2Fsheens/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Comcast%2Fsheens/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Comcast","download_url":"https://codeload.github.com/Comcast/sheens/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230374118,"owners_count":18216041,"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":[],"created_at":"2024-11-14T20:18:57.343Z","updated_at":"2024-12-19T04:07:23.552Z","avatar_url":"https://github.com/Comcast.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/Comcast/sheens.svg?branch=master)](https://travis-ci.org/Comcast/sheens)\n[![Documentation](https://godoc.org/github.com/Comcast/sheens?status.svg)](http://godoc.org/github.com/Comcast/sheens) \n\n![Sheens logo](doc/logo.png)\n\n# Welcome to Sheens\nFor implementing state machines.\n\nSheens is a lightweight and flexible framework for building and executing state machines. It is written in Go and is designed to be simple to use and highly scalable. \n\nWith Sheens, you can define state machines using a simple JSON-based syntax. You can then execute these using the Sheens runtime, which provides a highly optimized execution engine for running state machines at scale.\n\nWorkflows are a concept that can be supported by finite state machines. As a finite state machine engine, Sheens can be used to define a series of states and transitions between them, which can represent the different stages of a process or a sequence of events. Together, you might call them a workflow. Sheens working together like this can range from simple, linear sequences to more complex, event-driven systems, and they can be used to automate a wide range of tasks and processes. By using a well-designed library, such as the one Sheens provides, you can build workflows that are scalable, efficient, and easy to manage.\n\nIn addition to its powerful state machine capabilities, Sheens also includes support for timers, allowing you to define timed events and trigger actions based on them. Sheens also includes a number of other features and capabilities, including support for dynamic loading, automatic state machine validation, and much more.\n\nIf you're looking for a flexible and powerful tool for implementing these ideas, Sheens is the perfect choice. Whether you're building a simple sequential workflow or a complex, asynchronous system, Sheens provides the tools and capabilities you need to get the job done.\n\n# Messaging machines\n\n\u003ca href=\"specs/door.yaml\"\u003e\u003cimg src=\"doc/door-graph.png\" width=\"200\"\u003e\u003c/a\u003e\n\nSheens is an automation engine that hosts message-processing state\nmachines (also called \"sheens\").  These machines process and emit\nmessages efficiently and atomically.  The initial motivation was\nimplementing IoT-oriented home automations; however, Sheens has been\nuseful in other settings.\n\nSheens is easy to integrate via HTTP, WebSockets, MQTT, plain TCP, Go\nor C applications, or via other glue.  For example, we have integrated\nSheens with several internal services, [AWS\nIoT](https://aws.amazon.com/iot/), [Home\nAssistant](https://home-assistant.io/), and other frameworks.\n\nThe structure and behavior of sheens are amenable to\n[standardization](doc/rfc.md), and multiple\n[implementations](https://github.com/Comcast/littlesheens) are\nfeasible and practical.  The Sheens engine is highly programmable.\nSheen-oriented tools (debuggers, visualizations, monitoring,\nanalyzers) are often easy to implement.\n\n\n\u003cimg src=\"doc/sheens.png\" width=\"500\"\u003e\n\n## License\n\nThis repo is licensed under [Apache License 2.0](LICENSE).\n\n## Demo usage\n\nTo build, first install [Go](https://golang.org/doc/install).\n\nThen:\n\n```Shell\ngo get github.com/Comcast/sheens/...\ncd $GOPATH/src/github.com/Comcast/sheens # Or equivalent\necho '{\"double\":1}' | msimple -s specs/double.yaml \n```\n\nA bit fancier:\n\n```Shell\nmcrew -s specs -t :9000 \u0026\ncat cmd/mcrew/input.txt | nc localhost 9000\nkill %%\n```\n\nSee [`cmd/mcrew`](cmd/mcrew) for more discussion, and see the rest of\nthis README and [`doc/by-example.md`](doc/by-example.md) for a start\nat documentation.\n\nApplications will all use the [`core`](core) package (which has decent\n[godoc](https://godoc.org/github.com/Comcast/sheens/core)).  If an\napplication wants a little help with containers of machines, then the\n`crew` package might be a good start.  An application should provide\nits own message transport (both in and out), and an application should\nprovide its own persistence.  For simple example, see\n[`cmd/msimple`](cmd/msimple), which is a very simple single-machine\nprocess.  The example [`cmd/mcrew`](cmd/mcrew) demonstrates some\nadditional functionality.\n\n\n## Goals\n\nThis project attempts to provide automation gear that is\n\n1. Simple\n2. Robust and correct\n3. Debuggable, testable\n4. Efficient\n\nIoT is a motivating application area.\n\nOther objectives\n\n1. Pluggable action interpreters.  Actions can be written in a\n   language that's executed by pluggable components.\n2. Transport-agnostic.  Input from and output to any sort of messaging\n   services (e.g., HTTP, MQTT, CoAP).\n3. Pluggable persistence.\n4. Amenable to formal specification(s) (see below).\n5. Feasible [alternative implementations](https://github.com/Comcast/littlesheens) in other\n   languages.\n6. Modest resource requirements.\n\n\u003ca href=\"doc/homeassistant.svg\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/Comcast/sheens/master/doc/homeassistant.svg?sanitize=true\" width=\"400\"\u003e\u003c/a\u003e\n\n\n## Design\n\nA machine processes and emits messages.\n\n1. A machine's state consists of the name of a node and the set of\n   bindings (and a machine specification).  A machine's initial\n   bindings are its parameters.\n2. A machine's specification defines the structure and behavior of the\n   machine.  (See [`specs`](specs) for some example\n   specifications.)\n3. A state transition is determined by pattern matching against either\n   a pending message or current bindings.\n4. Actions ideally just generate messages and return new bindings.  A\n   good action has no side effects nor will it ever block on IO.\n6. A collection of machines is called a _crew_. A crew is\n   typically associated with a user account (or some agent).\n7. Machines within a crew can send messages to all other machines\n   in that crew or to a specific machine in that crew.\n   (Intra-crew messaging is optionally provided by a _nanobus_ via\n   a pluggable `Router`.)\n8. Machines can send messages to any service if routed appropriately.\n9. Action languages are pluggable.  Most examples here are based on\n   [goja](https://github.com/dop251/goja), which is an ECMAScript\n   implementation.\n\n\u003e \"Transmit the message to the receiver; hope for an answer some day.\" \n\u003e\n\u003e -[Talking Heads](https://play.google.com/music/preview/Tx4yvxloe6nc6ifnu77hd6n3ipe)\n\n## Definitions\n\n\u003cimg src=\"doc/by-example-1.png\" width=\"400\"\u003e\n\nThis section provides definitions for machines.  For an example-based\nexposition, see this work-in-progress\n[documentation](doc/by-example.md).\n\nGiven a _machine specification_ and some _initialization parameters_,\nwe can make a _machine_, which can perform _actions_ in response to\n_messages_.\n\nA machine specification consists of a map from _node names_ (just\nstrings) to _nodes_.  (See [`specs`](specs) for some example\nspecifications.)\n\nA node consists of an optional _action_ and a required _branching_\nspecification that consists of a branching type, which is either\n`message` or `bindings`, and a set of _branches_ to other nodes.\n\nAn action could be any function that accepts bindings and returns\nbindings.  Ultimately, an action can (or should) only construct\nmessages and return a new set of bindings.  Every action has a\ntimeout, which is enforced.\n\nEach branch consists of an optional _pattern_, optional _guard_, and a\nrequired _target_, which is the name of a node in the machine\nspecification.\n\nA _pattern_, which can include pattern variables, can be matched\nagainst an incoming message or current bindings.  See below for more\nabout patterns.  A _guard_ is an optional procedure that generates\nbindings (perhaps nil) from bindings.  If a guard returns no bindings,\nthen the branch isn't followed.\n\nA machine consists of its _current state_: the name of the current\nnode, the current bindings, and a pointer to the machine's\nspecification.  A machine's initial parameters become the machine's\nfirst bindings.\n\nA _crew_ is a group of machines associated with some agent.\n\n\n\u003e To try to be happy is to try to build a machine with no other\n\u003e specification than that it shall run noiselessly.\n\u003e\n\u003e -[Robert Oppenheimer](https://en.wikiquote.org/wiki/Robert_Oppenheimer)\n\n\n## Pattern matching\n\nTransitions from one node to another are driven by pattern matching,\neither against the current set of bindings or a pending message.\n\nA _pattern_ is a string, array, or map (perhaps with deep structure)\nthat can be matched against an incoming message or the current\nbindings.  A string in a pattern that starts with a `?` is a _pattern\nvariable_ that will be bound when a message or bindinings matches the\npattern.\n\nHere's a pattern with two pattern variables (`?here` and `?address`):\n\n```Javascript\n{\"person\": \"homer\",\n \"likes\": \"?here\",\n \"at\": {\"type\": \"residence\", \"address\": \"?address\"}}\n```\n\nThe string `?` (without anything else) is an anonymous pattern\nvariable that matches anything and is not based on input bindings or\nincluded in output bindings.\n\nA message matched against a pattern results in zero or more sets of\nvariable bindings.\n\nAs an example, the pattern\n\n```Javascript\n{\"a\":{\"b\":\"?x\",\"c\":\"?y\"},\"d\":\"?y\"}\n```\n\nmatches\n\n```Javascript\n{\"a\":{\"b\":1,\"c\":2},\"d\":2}\n```\n\nwith a set of bindings\n\n```Javascript\n[{\"?x\":1,\"?y\":2}]\n```\nWhen matching against a message, a map or set (array) value need not\nbe completely specified in the pattern.  This behavior is called\n*partial matching*.  Examples:\n\n\u003e Pattern `{\"a\": 1, \"b\": \"?x\"}` matches `{\"a\": 1, \"b\": 2, \"c\": 3}` even though the pattern does\n\u003e not contain a key `\"c\"`.\n\n\u003e Pattern `{\"a\": 1, \"b\": [\"?x\"]}` matches `{\"a\": 1, \"b\": [2,3]}` with bindings `[{\"?x\": 2}, {\"?x\": 3}]`.\n\u003e Note the multiple bindings.\n\nWith partial matching, backtracking can occur.  Also you might get\nmore than one set of bindings.\n\nAn array is treated as a set, which can also trigger backtracking.\nFor example, the pattern\n\n```Javascript\n{\"a\":[{\"b\":\"?x\"}]}\n```\n\nmatches\n\n```Javascript\n{\"a\":[{\"b\":1},{\"b\":2},{\"c\":3}]}\n```\n\nwith the bindings\n\n```Javascript\n[{\"?x\":1},{\"?x\":2}]\n```\n\nFor some more examples, see [`match/match.md`](match/match.md), which is\ngenerated by test cases.\n\nThe utility [`patmatch`](cmd/patmatch) is handy for testing patterns:\n\n\n```Shell\npatmatch -p '{\"a\":[{\"b\":\"?x\"}]}' -m '{\"a\":[{\"b\":1},{\"b\":2},{\"c\":3}]}'\n[{\"?x\":1},{\"?x\":2}]\n```\n\n### Experimental optional pattern variables\n\nIf a pattern variable starts with `??`, that variable (or its\nassociated property when in a map) is optional.  Examples:\n\n```Shell\npatmatch -p '{\"x\":\"??opt\",\"y\":\"?y\"}' -m '{\"y\":\"Y\"}'\n[{\"?y\":\"Y\"}]\n\npatmatch -p '{\"x\":\"??opt\",\"y\":\"?y\"}' -m '{\"y\":\"Y\",\"x\":\"X\"}'\n[{\"??opt\":\"X\",\"?y\":\"Y\"}]\n\npatmatch -p '[\"??maybe\",\"a\",\"b\"]' -m '[\"a\",\"b\",\"c\"]'\n[{\"??maybe\":\"c\"}]\n\npatmatch -p '[\"??maybe\",\"a\",\"b\"]' -m '[\"a\",\"b\"]'\n[{}]\n\npatmatch -p '[\"??maybe\",\"a\",\"b\"]' -m '[\"a\"]'\nnull\n```\n\n### Experimental matching inequalities\n\nAs an experimental feature, pattern matching supports numeric\ninequalities.\n\nThe input bindings should include a binding for a variable with a name\nthat contains either `\u003c`, `\u003e`, `\u003c=`, `\u003e=`, or `!=` immediately after\nthe leading `?`.  The input pattern can then use that variable.  When\nmatching, a value _X_ will match that variable only if the binding _Y_\nfor that variable satisfies the inequality with _X_ and _Y_ (in that\norder). In this case, the output bindings will include a new binding\nfor a variable with the same name as the inequality variable but\nwithout the actual inequality.\n\n(Yes, such a feature makes us stare down a slippery slope.)\n\nFor example, given input bindings `{\"?\u003cn\":10}`, pattern\n`{\"n\":\"?\u003cn\"}`, and message `{\"n\":3}`, the match will succeed\nwith bindings `{\"?\u003cn\":10,\"?n\":3}`.\n\nSee the [matching examples](match/match.md) for several\nexamples. (Search for \"inequality\".)\n\n\n\n## Processing\n\nA machine processes an input message by\n\n1.  Obtaining the machine's current node based on the current node\n    name and machine specification.\n\n2.  If the current node has branching of type `message`, then the\n    message is matched against each branch's pattern.  When there's a\n    match, the branch's guard (if any) is executed based on the\n    bindings that resulted from the match.  If the guard returns\n    non-nil bindings, the machine's current node name is set to the\n    branch's target, and the machine's current bindings is set to\n    those (non-nil) bindings.  When a branch doesn't have a guard, the\n    machine proceeds directly to the branch target.\n   \n    Branches are processed in the order given.\n\t\n3.  If the current node has branching of type `bindings`, then the\n    procedure above is executed except that the current bindings are\n    used in place of the input message.  The process is execute until\n    the branching are of type `message`, at which point the procedure\n    above is executed.\n\t\nWhen the machine arrives at a node that has an action, that action is\nexecuted. Action execution results in a new set of bindings, which\nwill replace the current bindings.  (An action is given the current\nbindings.)\n\nA node with an action must have branching of type \"bindings\", so\nprocessing can immediately proceed to the node's branches.  (This\nbehavior allows an action to have side effects, but we'd rather\nactions _not_ have side effects.)\n\nIdeally, actions can only emit messages.  In particular, in typical\nuse, an action can't even make an HTTP request!\n\t\n(You might not have noticed that pattern matching during branch\nevaluation can result in multiple sets of bindings (\"bindingss\").\nWhat to do in that case?  One approach: If match results in more than\none set of bindings, each extra set of bindings results in the\ncreation of a new machine!  Alternately: Try each set of bindings in\n(the arbitrary) order, and go with the first set of bindings that\nworks.  That's what the current implementations do, I think.\n\n\n## Discussion\n\n### No exceptions?\n\nRetry-like functionality is specified just as any other control flow\nis specified.  There are almost no exceptions.  (The only exception is\nwhen an internal error occurs, and, even in that case, a setting\ncontrols what happens next.\n\t\nFor example, the processor inject the error in bindings to allow for\nstandard, branching-based transitions based on the error.\nAlternative, the machine could automatically transition to an error\nnode.  (That behavior would be the only possibility if the error\noccured at a location that prevented branching-based error handling.)\n\n### Simple state model\t\n\nA machine's state is independent from other machines' states.\nMachines can run concurrently without any locking or synchronization.\n\nA machine operates sequentially.  Therefore, there are no concurrent\nstate mutations (within a single process).\n\nVirtuous actions have no side effects, and their processing is atomic.\nSuch an action can generate multiple outbound messages, but the\nprocessor would only send that batch on if the action terminated\nwithout error.  Using this behavior, an application can process a\nmessage using multiple machines and that entire processing can be\natomic.\n\t\n(Since action interpreters are pluggable, a system can of course\nprovide dangerous action interpreters, which can allow actions to do\nIO and other unholy things.  Yes, you could have a Bash-based action\ninterpreter.)\n\t\n### No internal blocking\n\nA virtuous machine action does not block on IO.  Therefore machine\nperformance and resource consumption is can be relatively predictable.\n\t\n(Again, since action interpreters are pluggable, a system can of course\nprovide dangerous action interpreters, which can allow actions to do\nIO and other unholy things.)\n\n### Future: Dispatch index\n\nWhen a message is presented to a set of machines (which could contain\nhundreds of machines), we'll want efficient dispatch to the subset of\nmachines that are waiting on `message` branching that will (likely)\nmatch the message.  Efficient dispatch will likely require an index;\n`util/index.go` heads that direction.  Other approaches are of course\npossible.\n\n### Tool-able\n\t\nMachines are amenable to debugging using machine-oriented\ndebugging tools that provide the usual operations: reset state,\nstep through transitions, back up, set breakpoints, etc.\n\nSince messages are message processors with exposed state, test tools\n(see [`cmd/mexpect`](cmd/mexpect) for an example) are relatively easy\nto write.  The `core` can be programmed functionality.  (There is no\n\"state\" in `core`!)\n\t\n### Atomic spec updating\n\nIf you want to update a machine specification (in a way that's\nbackward-compatible), you don't have to touch any user data.\nTherefore, you can give many machines a new specification with a\nsingle atomic swap (within a process).  See `core.Specter`.\n\t\n### Timers and QoS\n\t\nA system could have multiple timer services for different qualities of\ntimers.  For example, a _nanocron_ could implement timers with\ngoroutines (with various suitable limits). An external timer service\ncould provide durable timers.  Etc.\n\n## Formal methods\n\nThough you can happily use sheens without worrying about formalities, you can get formal with sheens if you want to.\n\n### System verification\n\nThe operation of a machine is amenable to formal specification (of evolving [precision](doc/rfc.md)) and [alternate implementations](https://github.com/Comcast/littlesheens). An interprising investigator could implement a sheens system and prove properties about it.  We've started an experiment with [Idris](https://www.idris-lang.org/) along these lines.\n\n### Action proofs\n\nIn the implementation in this repo, the interpreters for actions and guards are pluggable, so an application is free to provide its own interpreter(s).  For example, a conventional native code sheens system, which ideally has been subject to extensive testing, could offer an action interpreter that enables and/or requires proofs of useful properties, such as [being total](http://docs.idris-lang.org/en/latest/tutorial/theorems.html#totality-checking), not making undesired bindings, or only emitting messages with certain properties.\n\n### Statistical mechanics\n\n\u003cimg src=\"doc/viz.gif\"\u003e\n\nA sheen is not in general a _finite_ state machine. Techincally speaking, a machine is triple of node id, current bindings, and the machine's specification, and the space of bindings is in general not finite.  Therefore even holding the specification constant doesn't give a finite state.  However, a sheen step does have the [Markov property](https://en.wikipedia.org/wiki/Markov_property), which makes a wide range of analysis feasible.\n\nFor example, consider the projection of a sheen's state space to just the set of nodes defined in the sheens's specification.  We now have a finite set of states.  Of course, state transitions still have that Markov property.  So we can (say) build and maintain simple models of state transition counts over time: `count(FROM,TO) → N`. If there are `S` nodes, then that information can be represented as a vector of `S*S` integer components.  Assume we update that data virtually at some interval to reflect a state not changing during that interval.  (Of course, we can perform that computation in one go rather than performing some actual heartbeat-like activity.)  We could then accumulate a distribution of those vectors over time.  Finally, to finish this little example, we could watch for a 2\u0026sigma; (or whatever) excursion, which perhaps indicates a new regime or anomaly of some sort.\n\n### Note on atomicity and errors\n\nAs we mentioned above, a virtuous action performs no I/O.  Therefore, a single message (or even a batch of messages) can be processed atomically againts a set of machines. In general, the result is a structure that contains new states, which can include error states or conditions, and a set of sequences (of sequences) of messages. The caller can then decide what to do next.  If the processing resulted in no system-level error conditions, then all states are updated (hopefully atomically via the application's persistence system). Only then are the output messages actually forwarded to a system that can deliver them (with the required policies/guaranties).  If any system-level error condition occured, the application could retry all processing.  More economically, the application could retry only the specific failure. Either way, the application need not fear side effects from the previous attempt.  Of course, other approaches are possible.\n\n\t\n## Sheens in Shakespeare\n\n|||\n|-|-|\n|KING LEAR|How now! what art thou?|\n|||\n|SHEEN|A machine, sir.|\n|||\n|KING LEAR|What dost thou profess? what wouldst thou with us?|\n|||\n|SHEEN|I do profess to be no less than I seem; to serve\u003cbr\u003ehim truly that will put me in trust: to love him\u003cbr\u003ethat is honest; to converse with him that is wise,\u003cbr\u003eand says little; to fear judgment; to fight when I\u003cbr\u003ecannot choose; and to eat no fish.|\n|||\n|KING LEAR|What art thou?|\n|||\n|SHEEN|A very honest-hearted machine, and as poor as the king.|\n|||\n|KING LEAR|If thou be as poor for a subject as he is for a\u003cbr\u003eking, thou art poor enough. What wouldst thou?|\n|||\n|SHEEN|Service.|\n|||\n|KING LEAR|Who wouldst thou serve?|\n|||\n|SHEEN|You.|\n|||\n|KING LEAR|Dost thou know me, fellow?|\n|||\n|SHEEN|No, sir; but you have that in your countenance\u003cbr\u003ewhich I would fain call master.|\n|||\n|KING LEAR|What's that?|\n|||\n|SHEEN|Authority.|\n|||\n|KING LEAR|What services canst thou do?|\n|||\n|SHEEN|I can keep honest counsel, ride, run, mar a curious\u003cbr\u003etale in telling it, and deliver a plain message\u003cbr\u003ebluntly: that which ordinary men are fit for, I am\u003cbr\u003equalified in; and the best of me is diligence.|\n\n## Code of Conduct\n\nWe take our [code of conduct](CODE_OF_CONDUCT.md) seriously. Please\nabide by it.\n\n## Contributing\n\nPlease read our [contributing guide](CONTRIBUTING.md) for details on\nhow to contribute to our project.\n\n\n## References\n\n1. [The Actor Model](https://en.wikipedia.org/wiki/Actor_model)\n1. [Guards in OCaml](http://www2.lib.uchicago.edu/keith/ocaml-class/pattern-matching.html)\n1. [AWS Step Functions](https://aws.amazon.com/step-functions/) and [their state language](https://states-language.net/spec.html) \n1. Erlang's [`gen_statem`](http://erlang.org/doc/design_principles/statem.html)\t\n1. [Leslie Lamport](https://en.wikipedia.org/wiki/Leslie_Lamport) on [state machines](http://lamport.azurewebsites.net/pubs/state-machine.pdf)\n1. The [Rulio](https://github.com/Comcast/rulio) rules engine\n1. [Little Sheens](https://github.com/Comcast/littlesheens)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomcast%2Fsheens","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcomcast%2Fsheens","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomcast%2Fsheens/lists"}