Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a working state machine in F# using functions for representing states?

I'm trying to create a simple state machine in F# but having trouble getting two states with circular dependencies to work.
I have this state factory:

open System
let createState produceInput stateSwitchRule nextState = 
    let rec stateFunc() = 
        match produceInput() with
        | x when x = stateSwitchRule -> printfn "%s" "Switching state"; nextState()
        | _  -> printfn "%s" "Bad input. Try again"; stateFunc()
    stateFunc

which I use to create two mutually recursive states:

let rec pongState() = createState Console.ReadLine "go to ping" pingState
      and pingState = createState Console.ReadLine "go to pong" (pongState())

[<EntryPoint>]
let main argv = 
    pingState()
    0

When invoking pingState()and inputting "go to pong" the state is switched to pong. But when invoking inputting "go to ping" a null reference exception is thrown.
Is there anyway around this with the chosen approach or should I model it differently?

like image 768
Christian Avatar asked Oct 20 '22 23:10

Christian


1 Answers

This is what I did:

#nowarn "40"

open System

let createState produceInput stateSwitchRule nextState = 
    let rec stateFunc () = 
        match produceInput() with
        | x when x = stateSwitchRule -> printfn "%s" "Switching state"; (nextState()) ()
        | _  -> printfn "%s" "Bad input. Try again"; stateFunc()
    stateFunc

let rec pongState : unit -> (unit -> string) = createState Console.ReadLine "go to ping" (fun () -> pingState)
    and pingState : unit -> (unit -> string) = createState Console.ReadLine "go to pong" (fun () -> pongState)

#nowarn "40" to suppress the warning about checking recursively defined objects for initialization soundness, different type for the nextState function, otherwise the compiler complained about a value being evaluated as a part of its definition, and superfluous type annotations on states because FSI complained about them being inferred to be generic. Lots of complaints ;)

As for modelling it differently - I think I would wrap it in a type instead of using solely functions, it seems more natural. I guess using functions was the whole point here though.

like image 50
scrwtp Avatar answered Oct 27 '22 10:10

scrwtp