In the last few months, I’ve created an actor frameworks in which each actors is in functional reactive way in F#.
Basically each actor is created with the Elm Architecture, everything is strong typed, can be run on Dot Net Core, or in browser with javascript generated by Fable, also can be run on mobile or desktop devices with Xamarin or Dot Net.
It costed me much time and efforts to get it to current state, learned a lot along the way, will try to write something before I forget the details.
Some Background (Feel Free to Skip)
Back in March, I started a new projects that needs server side logic and web or native apps as clients.
My first attempt was to tweak the server side framework that I used on games before for it. It was my dap context in C# running on top of Orleans actor framework. Orleans works purely in async way as an actor system, though my libs was originally used in games, and was mainly in sync mode, so I ended us have some hacky solution to mix them together, in the game usage it seems to be fine, didn’t caused me much problems. Though the new project involves many external HTTP and WebSocket access, so the framework gets in the way, may the code messy with some nasty delay to link sync and async sides together.
Also there are quite some complex data structures in it as well, I tried to use the metadata code generator (use Microsoft Bond as data definition language) I created for game dev with it as well, though not really happy either, for games’ metadata, there are a few rather complex data structures that may have many instances, and the data type itself usually are combined by simple types. Though in the new project, the logic is quite complex, and the number of data structure is quite big, it’s very annoying if I want to create Bond definitions for everything.
So after a couple of week patching the exist framework, and fighting with some nasty bugs with the patching, and need more patchs on top of it, I decided to take a new approach.
At first, I planned to take full Orleans approach, the problem was that being an (virtual) actor framework, I still need to find my way to organize these actors together in a nice way. Also being a server-side framework, it can not really help for the client side, though I really don’t want 2 languages if possible.
So it become clear that I need some framework again, pure async one to work nicely with Orleans and GUI apps, easy to access external services through HTTP and WebSocket, single language for both client and server side.
Why F#
I did use Elm in some front-end small projects in last couple of years, which is great, use Records and Unions can describe data structures very nicely, and refactoring with Elm feels so good. Though server-side Elm is missing ATM, did some hacky way to render HTML pages with it running in node, though it’s not ready for heavy logic yet.
I also did some experiments with Purescript before, which is very similar to Haskell, transcompiled to javascript, so can be run on browsers, nodejs, and react-native. The language is well-designed, harder to learn compared to Elm. Seems Purescript is even less popular than Elm, so it might be a risky choice, also running the backend on nodejs is not ideal to me.
Dot Not Core is really nice, Orleans is very nice too, I really want to keep work on top of them, so I did the framework in F# eventually.
I’ve been reading on F# before, which seems a compromised design back then, it’s not a pure function language, support complete Object-Oriented as well, also can use mutable values if you wanted to. on the other hand, these design choices make it quite practical, and can took advantage of the much bigger C# libraries when need to integrate with third-parties.
Fable is a transcompiler for F# to generate javascript codes, then it can be run in browser or react native apps. It’s quite nice, there are certain limitations and some small things to be considered, but after the development environment been setup properly, it works as expected.
Another bonus to me is that I am used to generics in C#, generics in F# is as powerful, and actual codes can be much shorter.
Elmish (Functional Reactive in F#)
The Elm Architecture is really nice way to write logic, the idea is that you describe type of model and message, the running is just a serious of messages received and versions of models according to these messages.
In ideal situation the model should be immutable, then it’s quite easy to get serialized, which can leads to powerful features such as persistent app states, and time travel debugger.
F# community had created similar approach as Elmish, which is quite simple actually, was created to be used in Fable, then refactored to be used in normal F# as well.
The essential idea is that logic will check incoming message, create a new model based on the current one, any might create new messages that will be send into itself later. Of course it’s much more complex in real world usage, though technically it’s mostly about how an update is implemented, the method signature looks like this:
type Update<'model, 'msg> = 'msg -> 'model -> 'model * Cmd<'msg>
Actors and Runtime
The Elmish approach did imply an actor like way, messages can be received one-by-one, and sending messages should be the only way to interact with the inner system, which is basically an actor.
In Elm’s cases, the whole application is the only actor, use subscriptions and system libraries (such as HTTP accessing) to generate messages that send back in the future.
Elmish provide simple implementation with F#’s MailboxProcessor, you can create multiple programs if you want, it provide view rendering to show stuffs in browsers (or react native, or other GUI system such as Xamarin Forms). It’s also usually the only actor.
On the contrary, actor framework normally have many actors, and usually not having GUI elements with them.
My tasks here is to create a runtime for my actors. which will handle the message passing, and state management, also optional GUI integration.
I did some abstraction with the runtime, so it’s implementation can be on top of different platforms, currently I’ve have one with MailboxProcessor, later will add Orleans and maybe experiment with ProtoActor later.
Also did some abstraction with the runner object, so I can organize actor-specific helpers easily, e.g. one actor might want to access or create other actors, one way is to make this a system wise singleton, though that means it’s not easy to isolate actors from each other. another example is loggers, I want each actor to have its own logger, so the code need someway to access the logger.
So my update looks like:
type Update<'runner, 'model, 'msg> =
'runner -> 'msg -> 'model -> 'model * Cmd<'msg>
This is the biggest difference between my framework and Elmish, also this make the implementation much harder, I feel it worth the effort, will have more details on this topic later.
Where is The Code?
It’s not ready yet, it’s working, can be used as nuget packages (on private source now), though not yet ready to be open-sourced, currently still in the quick iteration mode.
Will write more blogs first, then when it’s stable enough, will probably make it public.