Renamed to bellroy/elm-actor-framework. It is published under that name, and new versions come out there.
Versions published as tricycle/elm-actor-framework still install under that name. 2 versions of 6 come from the new location (see their notes). 4 versions could not be recovered and cannot be installed.
Unavailable. Nobody can download this version any more, with elm or with the Lamdera compiler: github.com/bellroy/elm-actor-framework exists but has no tag 1.0.1; Software Heritage not asked yet (this run's --swh-calls were used up); no fork with tag 1.0.1 found (1 same-name repos checked); no cached copy. Use another version.
This library allows you to more easily re-use components between Elm applications by moving state, views and other logic into components themselves. Helping to avoid the top heavy application that the traditional Elm architecture can lead to and that becomes hard to maintain.

Start with the (tutorial)(#Tutorial) in this Readme file.
The API documentation is hosted on the Elm package website.
There are three examples in the example folder.
The output can be seen online and it's easy to run them locally;
cd example.yarn installyarn run start:counteryarn run start:countersyarn run start:spahttp://localhost:8000 in your desired browser.Actors make up ideal components that can be used on a template.
It's easiest, or maybe just easiest for me, to start at the "inside" when describing how the Elm Actor Framework works, or better; how to use it.
The concept of a Component within this little framework is as followed;
A component describes what what its state looks like (Model), its initial state and action (init) , how it can update its state based on messages (MsgIn) and what it outputs (view).
Hopefully this sounds very familiar. It follows the same pattern as any other
Elm framework. Without too much work a simple Elm application could be ported to
become a Component.
A neat thing about Components is that they could be interchangeable between
different application.
Let's start with setting up a simple Counter Component.
The Component is a record which signature can be imported from the
Framework.Actor module. It looks almost the same as Elm.Browser's embed
function so this is going to be easy!
File: Component/Counter.elm
module Component.Counter exposing (Model, MsgIn, component)We are going to use the Component record type from this package
import Framework.Actor exposing (Component)This Component will output Html, but you decide what your Component will output;
You could just output Strings for example or perhaps you prefer using elm-ui!
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)Our Counters Model will be an alias for an Integer.
type alias Model =
IntOur Counter can Increment or Decrement its state using these msg's.
I've called them MsgIn because a component can also have msgOut's but we are
not going to use those for our first go.
type MsgIn
= Increment
| DecrementOur Component doesn't use appFlags so we can tell it to never expect
anything by giving it the () type. We also opt-out of using msgOut using
the same technique
component : Component () Model MsgIn () (Html msg) msg
component =
{ init = init
, update = update
, subscriptions = always Sub.none
, view = view
}The initial state of our Counter is 0. We aren't returning any msgOuts or cmd's just yet.
init : a -> ( Model, List (), Cmd msg )
init _ =
( 0, [], Cmd.none )Given a msgIn and our current state (Model) we can return a new updated state
update : MsgIn -> Model -> ( Model, List (), Cmd MsgIn )
update msgIn model =
case msgIn of
Increment ->
( model + 1, [], Cmd.none )
Decrement ->
( model - 1, [], Cmd.none )Our view function renders our counter's current value and two buttons that can
decrement or increment the counters value.
The view function gets a function provided that knows how to turn internal
MsgIn's into higher level msg's. We need to use Html.map is this case to
comply with the expected return type.
view : (MsgIn -> msg) -> Model -> a -> Html msg
view toSelf model _ =
div []
[ Html.button [ HtmlE.onClick Decrement ] [ Html.text "-" ]
, Html.span [] [ String.fromInt model |> Html.text ]
, Html.button [ HtmlE.onClick Increment ] [ Html.text "+" ]
]
|> Html.map toSelfIn the module above we have described a simple Counter Component that
starts with an integer value of 0 and decrements or increments its value by 1
when a user clicks one of the buttons.
Now that we have created our first Component we can start setting up the rest of our Program.

File: Main.elm
module Main exposing (main)
import Html exposing (div)Elm's main function expects a Program to be returned. The
Framework.Browser module offers multiple ways of creating such a Program, and
mirrors the behaviour described on Elm's - Browser package.
import Framework.Browser exposing (Program, element)We will also require the Framework.Message and Framework.Actor modules later on.
import Framework.Actor exposing (Actor, Pid, Process, fromComponent)
import Framework.Message exposing (FrameworkMessage, addToView, batch, noOperation, spawn)Import the Counter we just created, not that the Counter module itself doesn't depend on any types we will define here. The counter we just created could come from a different application and we can reuse our Counter on a different application as well.
import Component.Counter as CounterWe are going to use the element function here that on its turn uses the Elm's - Browser package to create our Elm application. https://package.elm-lang.org/packages/elm/browser/latest/Browser#element
For now we skip the elmFlags appFlags and appAddresses definitions by
supplying ().
main : Program () () () AppActors AppModel AppMsg
main =
Browser.element
{ factory = factory
, apply = apply
, init = init
, view = view
}Our Program signature tells us we need to supply it a type representing our
appActors. This is a type we use that represents our Actors within our
application.
type AppActors
= CounterOur components hold define and deal their own state (Model) but it's our
application that eventually needs to store this. appModel is a type that
can wrap our components Models.
type AppModel
= CounterModel Counter.ModelJust like the appModel our application has to deal with all of our components
msgIn's. In a similar fashion of how our AppModel wraps our Counter.Model;
AppMsg wraps our Counter.MsgIn.
type AppMsg
= CounterMsg Counter.MsgInNow that we've dealt with our required types, we can start looking at implementing the functions we promised to our Browser.element function starting with the factory function.
This is were things become a little bit more tricky. I've tried writing this in a more "discovering" order then perhaps logical or chronological.
The first next "clue" we have is our missing factory implementation.
A factory's signature looks like;
appActors -> ( Pid, appFlags ) -> ( appModel, FrameworkMessage appFlags appAddresses appActors appModel appMsg )
We've already handled the type variables we see here when we defined our Program.
And by creating a Msg type alias we don't have to repeat the FrameworkMessage
type every time.
Our component doesn't care about it's Pid so we can simplify our implementation a little but by ignoring that Tuple all together.
type alias Msg = FrameworkMessage () () AppActors AppModel AppMsg factory : AppActors -> a -> ( AppModel, Msg )
Now the signature is a bit easier to talk about;
Factory takes an AppActors and should give us back a function a -> (AppModel, Msg).
Now if we search for that signature on our Api documentation we'll find that
the Actor record on the Framework.Actor module provides an init function
with that exact signature.
So now we need to think about how we can get an Actor to use on our factory
function. On the same Actor module we can find the fromComponent function.
The fromComponent function takes a record of functions that allow a
Component to progress in to an Actor.
The record requires the following functions;
toAppModel: componentModel -> appModel
Given an componentModel return an appModel... Hey we've already got this
covered! Our AppModel's CounterModel takes Counter.Model as a value.toAppMsg: componentMsgIn -> appMsg
Very similar to toAppModel; We can use AppMsg's CounterMsg here.fromAppMsg: appMsg -> Maybe componentMsgIn
This is almost the opposite of toAppMsg, given an AppMsg we might be able
to return a componentMsgIn (a Counter.Msg).onMsgOut: { self: Pid, msgOut: componentMsgOut } -> FrameworkMessage ppFlags appAddresses appActors appModel appMsg
Our component doesn't have any msgOut's so we can just return a NoOp from
the Framework.Message module here to comply with the requested return type.The signature of an Actor combines a few type variables that we can get from our
Component and a few that we have defined here on our actual App.
Actor appFlags componentModel appModel output frameworkMs
Knowing all this we can define our actor based on our Counter Component.
I am going to call our actor counter.
counter : Actor () Counter.Model AppModel (Html Msg) Msg
counter =
fromComponent
{ toAppModel = CounterModel
, toAppMsg = CounterMsg
, fromAppMsg = \(CounterMsg msgIn) -> Just msgIn
, onMsgOut = \_ -> noOperation
}
Counter.componentNow we have turned our Component in to an Actor by providing functions that
let our Component understand how to "deal" with our Application level types.
This let's us continue with the factory implementation; we were searching for
a function that provides us with the required signature and by creating our
counter Actor we have done just that.
type alias Msg =
FrameworkMessage () () AppActors AppModel AppMsg
factory : AppActors -> ( Pid, () ) -> ( AppModel, Msg )
factory actor =
case actor of
Counter ->
counter.initNow we can obviously just return counter.init straight away, we don't need
to case match here. But imagine having more then one Actor then this is the
way to handle multiple of them.
Cool, so one down. Next is the apply function. The (simplified) signature
looks like;
apply : appModel -> Process appModel output Msg
So in our case; given an AppModel return a Process AppModel (Html Msg) Msg
Well first, what is an Process within the scope of this package?
An Process means an Actor + State. So in other words a "running" Actor.

We can get a Process from our freshly created Actor by using its apply
function and providing its componentModel.
apply : AppModel -> Process AppModel (Html Msg) Msg
apply appModel =
case appModel of
CounterModel counterModel ->
counter.apply counterModelThat's it, to easy!
Next up; init
Just like the init function on our Component or on a typical Elm application
init allows us to set an initial state of our Application.
Unlike the other mentioned init functions our init should return a
FrameworkMessage though instead of a Model. But fear not because by calling
these messages we update an predetermined framework model.
Without using init we haven't actually "started" our Counter Actor yet.
And Let's straight away utilise our set up here and spin up two Counter's
at start up that each hold their own state.
We can start our Actors by spawn-ing them.
spawn on the Framework.Message module takes 3 arguments.
elm appFlags
Just like a typical Elm app your actors could receive some flags at start up.
We already set our app doesn't use though so we'll leave it to () for now.appActors
The actor you want to spawn(Pid -> Msg)
A callback function that will provide the newly created Pid.
The Pid is the unique identifier of a process that can be used to send
message to.When we do spawn our Counter's we should also let the application know
what to do with the output. For now we will add the output of our Counter's
straight on our application view using addToView. In other cases you might
want a different Actor spawn different Actors.
We can use batch to batch multiple FrameworkMessage's in to a single one.
The batch will be performed in order.
init : flags -> Msg
init _ =
batch
[ spawn () Counter addToView
, spawn () Counter addToView
]We're nearly there. All is left is to provide our main function with a view
This should be very straight forward.
view : List (Html Msg) -> Html Msg
view =
div []An near identical demonstration of this Counter example can be found inside the
example folder. There are also two more examples listed there that
progressively utilise more of the frameworks capabilities.