I think this question affects the same area, but I do not see how this can be applied to my situation. General response from agent / mail server processor?
Here is the background. I have some kind of state, let's just say, now it contains only a list of players. Could be more, for example. Games, etc. I also have an initial country that has no players.
type Player = {Name: string; Points: int} type State = {Players: Player list} let initialState = {Players = []}
I have two types of messages that I need to deal with. Queries, which are functions that map a state to a value, but do not change state. For example. Return an int showing the highest point rating.
And commands that produce a new state, but can return a value. For example, add a new player to the collection and return the identifier or something else.
type Message<'T> = | Query of (State -> 'T) | Command of (State -> 'T * State)
And then we have a model that can respond to messages. But which, unfortunately, uses mutable state, I would prefer to use MailboxProcessor and the message loop.
type Model(state: State) = let mutable currentState = state let HandleMessage (m: Message<'outp>) = match m with | Query q -> q currentState | Command c -> let n, s = c currentState currentState <- s n member this.Query<'T> (q: State -> 'T) = HandleMessage (Query q) member this.Command<'T> (c: State -> 'T * State) = HandleMessage (Command c) // Query Methods let HowMany (s: State) = List.length s.Players let HasAny (s: State) = (HowMany s) > 0 let ShowAll (s: State) = s // Command Methods let AddPlayer (p: Player) (s: State) = (p, {s with Players = p::s.Players}) let model = new Model(initialState) model.Command (AddPlayer {Name="Sandra"; Points=1000}) model.Query HasAny model.Query HowMany model.Query ShowAll
Obviously, it would be nice if this State argument itself was general. But one step at a time.
All I was trying to replace was that the modified currentState using MailboxProcessor failed. The problem is the Generics and Static nature of F #, but I cannot find a way around this.
The following steps do not work, but it shows what I would like to do.
type Player = {Name: string; Points: int} type State = {Players: Player list} let initialState = {Players = []} type Message<'T> = | Query of (State -> 'T) * AsyncReplyChannel<'T> | Command of (State -> 'T * State) * AsyncReplyChannel<'T> type Model(state: State) = let innerModel = MailboxProcessor.Start(fun inbox -> let rec messageLoop (state: State) = async { let! msg = inbox.Receive() match (msg: Message<'outp>) with | Query (q, replyChannel) -> replyChannel.Reply(q state) return! messageLoop state | Command (c, replyChannel) -> let result, newState = c state replyChannel.Reply(result) return! messageLoop(newState) } messageLoop initialState) member this.Query<'T> (q: State -> 'T) = innerModel.PostAndReply(fun chan -> Query(q , chan)) member this.Command<'T> (c: State -> 'T * State) = innerModel.PostAndReply(fun chan -> Command(c, chan)) // Query Methods let HowMany (s: State) = List.length s.Players let HasAny (s: State) = (HowMany s) > 0 let ShowAll (s: State) = s //// Command Methods let AddPlayer (p: 'T) (s: State) = {s with Players = p::s.Players} let model = new Model(initialState) model.Command (AddPlayer {Name="Joe"; Points=1000}) model.Query HowMany model.Query HasAny model.Query ShowAll