Tune docs
Confirm returns shawn
Tune code design and docs
The goal is to have something like funcool/potok
but without all the
RX cruft. That was the orginal plan anyway. But through the development the
ev
functionality was added to the Janet and so right now Shawn is more wrapper
and model for the simple ev
abstraction.
Shawn is the basic building block, which contains all the data needed for the running program's domain.
The only changes that you can do to Shawn are through confirming the Act.
You can attach Cocoons to the Shawn with long running processes, which do not fit into stream processing.
Shawn consists of the three encapsulated parts:
watch
method.ev
channel to supervise the Cocoons.For most of the time, your concern is only the Envelope.
The Shawn is any table with the prototype of
Shawn. The first thing
to start with Shawn is to initialize it. For this purpose there is
[inititiaze
]. The function takes two arguments:
envelope
which must be a table and is the initial value of the state.on-error
which must be a function and is used as the error handler when
calling one of the Act's methods.
The function returns the new initialized Shawn. Envelope is the datastructure,
where Shawn's state is held.The main mechanism of interaction with the Shawn is confirming. For this purpose
there is [confirm
]. This function takes a variable amount of the acts
which you want to confirm.
The Act
is Janet
object with four methods:
:spy
- this method should return Snoop of array of them. Snoops are run
after every :update
method of every act.:update
- mechanism to change anything in the Envelope. The function
takes an Act and the Envelope as arguments. It mutates the Envelope.:watch
- mechanism to add new Acts to Tide. Watchable are Act, Array of Acts
or fiber. The function takes an Act, Envelope, and Tide as its arguments. It
is antipattern to mutate envelope in the watch method.:effect
- mechanism to perform the side effects. The function takes an Act,
Envelope, and Tide as its arguments. It is antipatter to mutate any of them.There are two utility methods for creating acts:
make
function, which takes a table and sets its prototype to Act
.defact
macro, which defines the new act through make under supplied
name.There is also the valid?
function, which checks if the act is valid.
Snoop is similar to the Act, but it is not removed after one call. Its :snoop
method is called after every :update
method of every Act. This method call
should return Acts, which are added to the Tide as is done for the call to
:watch
method.
This is main mechanism for the reactivity.
The functionality described above is good for reactive stuff. First use case is where there is inital stack of acts and you model their flow. Second use case is for programs which reacts to some external inputs like user input.
Cocoons solves the situation, where you have code which runs on the event loop independetly from the stream processing, which are dispatching acts to the shawn.
When you stop confirming, you can still wait, by calling :admit
method
of the Shawn, for this code to run and get its results or even confirm acts
it produces.
Cocoons are advanced topics, not needed by everyone, so they are described in bigger detail in TECH.md.
TBD
There is evolving TECH.md document with more implementation details and other usual food for thougt.
Run tests with jpm test
or continuously with watch-test
. For watching you
need fd
and entr
.
If you want to try shawn in the repl:
➜ janet -l shawn -l shawn/act
Janet 1.13.2-dev-local linux/x64 - '(doc)' for help
repl:1:> (def shawn (init-shawn @{:counter 0}))
@{:envelope @{:counter 0}}
repl:2:> (defact IncreaseCounter @{:update (fn [_ envelope] (update envelope :counter inc))})
@{:update <function 0x563D27074CA0>}
repl:3:> (defact PrintCounter @{:effect (fn [_ envelope _] (print "Counter is: " (en
velope:counter)))})
@{:effect <function 0x563D270761C0>}
repl:4:> (def inc-and-print (make @{:watch (fn [_ _ _] [IncreaseCounter PrintCounter])}))
@{:watch <function 0x563D27077140>}
repl:5:> (:confirm shawn inc-and-print)
Counter is: 1
@{:processing false :envelope @{:counter 1}}
There are more examples in the examples directory. You can read more about them in README.