2013-08-14 45 views
10

Jaki jest ogólny sposób wdrożenia skończonej maszyny stanów (lub skończonego przetwornika stanu) w Scali?Generalna maszyna skończona (przetwornik) w Scali

Często znajduję się w potrzebie wdrożenia maszyny państwowej. Moja Typowa implementacja wygląda

object TypicalFSM { // actually — finite state transducer 
    type State 
    case object State1 extends State 
    case object State2 extends State 
    type Message 
    case object Message1 extends Message 
    type ResultMessage 
    case object ResultMessage1 extends ResultMessage 
} 

import TypicalFSM._ 

class TypicalFSM extends ((Message) =>Seq[ResultMessage]){ 
    var state:State = State1 

    def apply(message:Message):Seq[ResultMessage] = (state, message) match { 
    case (State1, Message1) => 
     state = State2 
     Seq(ResultMessage1, ResultMessage2) 
    } 
} 

Co lubię jest zmienny var co sprawia, że ​​nić rozwiązanie niebezpieczne. Również topologia FSM nie jest jasna.

  1. Jak tworzyć FSM w sposób funkcjonalny?

  2. To również byłby bardzo dobry rysować FSM-Graph w .dot format

  3. Akka FSM ma dobre właściwości pozwalające skojarzyć niektóre dane z państwa, nie tylko podając nazwę obiektu. Jest to również doceniane. (Jednak Akka FSM nie zawsze jest wygodny w użyciu, jak to jest asynchroniczne i czasami nieco wagi ciężkiej.)

+0

FSM mogą być piękne, gdy są wyrażone jako funkcje rekurencyjne. Prawdziwe ogonki są jednak kluczowe, więc Scala ich nie przerwie. Aby ominąć 'var', po prostu zwróć następny stan wraz z komunikatami i kontynuuj karmienie funkcji w sobie. Skutecznie budujesz typ "State". –

+0

Struktura Akka ma przydatną implementację automatu stanów, ale jest raczej zależna od wysyłania komunikatów wokół systemu aktora. Możesz przeczytać więcej [tutaj] (http://doc.akka.io/docs/akka/2.2.3/scala/fsm.html) –

Odpowiedz

7

Prawdopodobnie nie jest to, czego szukasz, ale myślę, że to ciekawa koncepcja.

object TypicalFSM { 

    sealed trait State 
    final class State1 extends State 
    final class State2 extends State 

    sealed trait Message 
    case class Message1(s: String) extends Message 
    case class Message2(s: String) extends Message 

    sealed trait ResultMessage 
    object ResultMessage1 extends ResultMessage 
    object ResultMessage2 extends ResultMessage 
} 

import TypicalFSM._ 

case class Transformation[M <: Message, From <: State, To <: State](
    f:M => Seq[ResultMessage]) { 

    def apply(m:M) = f(m) 
} 

object Transformation { 

    implicit def `message1 in state1` = 
    Transformation[Message1, State1, State2] { m => 
     Seq(ResultMessage1, ResultMessage2) 
    } 

    implicit def `message1 in state2` = 
    Transformation[Message1, State2, State2] { m => 
     Seq(ResultMessage1) 
    } 

    implicit def `message2 in state2` = 
    Transformation[Message2, State2, State1] { m => 
     Seq(ResultMessage2) 
    } 
} 

class TypicalFSM[CurrentState <: State] { 

    def apply[M <: Message, NewState <: State](message: M)(
    implicit transformWith: Transformation[M, CurrentState, NewState]) = { 

    this.asInstanceOf[TypicalFSM[NewState]] -> transformWith(message) 
    } 
} 

Wykorzystanie byłoby tak:

def test() = { 
    val s1 = new TypicalFSM[State1] 
    // type of s1: TypicalFSM[State1] 

    val (s2, r1) = s1(Message1("m1")) 
    // type of s2: TypicalFSM[State2] 

    val (s3, r2) = s2(Message1("m1")) 
    // type of s2: TypicalFSM[State2] 

    val (s4, r3) = s2(Message2("m2")) 
    // type of s2: TypicalFSM[State1] 

    // val (s5, r4) = s4(Message2("m2")) 
    // Fails with: 
    // 'No transformation available for TypicalFSM.Message2 in TypicalFSM.State1' 
    // type of s5: TypicalFSM[State1] 
} 

Twój przypadek użycia zdecydowanie określenia struktury kodu w tej koncepcji. Przypadku użycia naprawdę określa, ile informacji o typie chcesz zachować.

Ta koncepcja, ponieważ stan jest utrzymywany przy użyciu systemu typów, a nielegalne przejścia są zgłaszane podczas kompilacji.

+1

Interesujące podejście. Ściśle wpisane stany. Jednak myślę, że podpis Transformacji powinien być: transformacja klasy case [M <: Wiadomość, Od <: Stan, Do <: Stan] ( f: M => (Do, ResultMessage)) –

+1

@ArseniyZhizhelev Zgadzam się, Po prostu poszedłem za twoim przykładem. Teoretycznie możesz powiązać typ wyjścia z typem komunikatu. Naprawdę zależy od zastosowania, jak daleko chcesz się posunąć z typami w Scala, haha. – EECOLOR

+0

Bardzo interesujące ... ale kiedy wrócę s1 lub cokolwiek innego, jak mogę to sprawdzić? Załóżmy taką metodę: def typOf [T: TypeTag] (t: T) = reflect.runtime.universe.typeOf [T], kogo mam określić stan/odpowiedź zwróconą przez TypicalFSM, aby móc wykonać akcję? typeOf (s1) mecz {??? } – j3d