Definitions of recursive variables in Python and F # (maybe OCaml too)

Given these declarations like F # ...

type Message =
    | MessageA
    | MessageB
    | MessageC
    | MessageD

type State = {
    Name:string
    NextStateMap: Map<Message,State>
}

... there is an equally expressive definition of this particular state machine ...

let rec state0 = { Name = "0"; NextStateMap = Map.ofList [ (MessageA,state1); (MessageB,state2)] }
    and state1 = { Name = "1"; NextStateMap = Map.ofList [ (MessageB,state3)] }
    and state2 = { Name = "2"; NextStateMap = Map.ofList [ (MessageA,state3)] }
    and state3 = { Name = "3"; NextStateMap = Map.ofList [ (MessageC,state4)] }
    and state4 = { Name = "4"; NextStateMap = Map.ofList [ (MessageD,state5)] }
    and state5 = { Name = "5"; NextStateMap = Map.empty}

... with Python?

Note that through “rec” we did not need to make assignments in the order defined by the topological type ... (for example, state0 is defined in terms of state1, although state 1 is defined later).

PS The ability to use strings as state identifiers ...

stateMachine = {
   "0" : { "A":"1", "B":"2"},
   "1" : { "B":"3" },
...

... leaves open a window with invalid keys (that is, invalid message qualifiers on the destination machine).

+5
source share
2 answers

Python , , . :

state0 = State("0")
state1 = State("1")
... and so on ...
state0.next_states = {message_a: state1, message_b: state2 }
state1.next_states = {message_b: state3}
... and so on ...
+5
## a generic state machine framework ###################

class Message(object):
    """
    This represents a message being passed to the
    state machine.
    """
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return "Message(%r)" % self.name
    def __call__(self, smap):
        try:
            return smap[self]
        except KeyError:
            raise Exception("invalid message: %s vs %s"
                            % (self, smap))

class MessageFactory(object):
    """
    Since python doesn't have symbols, this automagically
    creates the messages for you. (It purely for
    convenience, and you could just as easily instantiate
    each message by hand.
    """
    cache = {}
    def __getattr__(self, name):
        return self.cache.setdefault(name, Message(name))

class StateMachine(object):
    """
    This keeps track of the state, of course. :)
    """
    def __init__(self, state):
        self.state = state
    def __call__(self, msg):
        self.state = self.state(msg)


## how to set it up: ###################################

msg = MessageFactory()
state =\
{
    0 : lambda m: m({ msg.A : state[1],
                      msg.B : state[2] }),
    1 : lambda m: m({ msg.B : state[3] }),
    2 : lambda m: m({ msg.A : state[3] }),
    3 : lambda m: m({ msg.C : state[4] }),
    4 : lambda m: m({ msg.D : state[5] }),
    5 : lambda m: m({ }),
}

## how to use it: ######################################

s = StateMachine(state[0])
s(msg.A)
assert s.state is state[1]
0

All Articles