2013-08-14 35 views
10

Qual è il modo generale di implementare una macchina a stati finiti (o trasduttore di stato finito) in Scala?Macchina a stati finiti generali (trasduttore) in Scala

Spesso mi trovo in difficoltà per l'implementazione della macchina di stato. La mia tipica implementazione sembra

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) 
    } 
} 

Quello che mi piace è il mutabile var che rende il filo soluzione non sicuri. Anche la topologia dell'FSM non è chiara.

  1. Come creare FSM in modo funzionale?

  2. Sarebbe anche molto buona per disegnare FSM-grafico in .dot format

  3. Akka FSM ha una buona proprietà di permettere di associare alcuni dati con uno Stato, non solo dando un nome di oggetto. Questo è anche apprezzato. (Tuttavia, Akka FSM non è sempre comodo da usare in quanto è asincrona e talvolta una pesante bit.)

+0

Gli FSM possono essere belli se espressi come funzioni reciprocamente ricorsive. La vera coda è fondamentale, tuttavia, in modo che Scala non lo tagli. Per evitare il tuo 'var', basta restituire lo stato successivo insieme ai messaggi, e continuare ad alimentare la funzione in se stesso. Stai effettivamente costruendo il tipo 'State'. –

+0

Il framework Akka ha un'implementazione della macchina di stato utile, ma dipende piuttosto dall'invio di messaggi attorno a un sistema di attori. Puoi leggere di più [qui] (http://doc.akka.io/docs/akka/2.2.3/scala/fsm.html) –

risposta

7

Questo non è probabilmente quello che stai cercando, ma penso che sia un concetto interessante.

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) 
    } 
} 

Uso sarebbe come questo:

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] 
} 

vostro caso d'uso sarebbe fortemente determinare la struttura del codice in questo concetto. Il caso d'uso determina realmente la quantità di informazioni di tipo che si desidera conservare.

I questo concetto perché lo stato viene mantenuto utilizzando il sistema di tipi e le transizioni illegali vengono segnalate in fase di compilazione.

+1

Approccio interessante. Stati rigorosamente tipizzati. Tuttavia, penso che la firma di Transformation dovrebbe essere: case class Transformation [M <: Message, From <: State, To <: State] ( f: M => (To, ResultMessage)) –

+1

@ArseniyZhizhelev Sono d'accordo, Ho appena seguito il tuo esempio. In teoria è possibile associare il tipo di output al tipo di messaggio. Dipende davvero dal caso d'uso fino a che punto si vuole andare con i tipi in Scala, haha. – EECOLOR

+0

Molto interessante ... ma una volta che torno s1 o qualsiasi altro stato, come faccio a controllarlo? Assumiamo un metodo come questo: def typeOf [T: TypeTag] (t: T) = reflect.runtime.universe.typeOf [T], chi devo determinare lo stato/la risposta restituita da TypicalFSM in modo che possa intraprendere un'azione? typeOf (s1) corrisponde {??? } – j3d