2013-08-14 3 views
10

Каков общий способ реализации конечного автомата (или преобразователя конечного состояния) в Scala?Общая машина конечного состояния (преобразователь) в Scala

Я часто нуждаюсь в реализации государственного аппарата. Моя типичная реализация выглядит

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

Что мне не нравится это изменяемые var что делает нить решения небезопасной. Также топология FSM не ясна.

  1. Как создать FSMs функциональным способом?

  2. Это также было бы очень хорошо, чтобы нарисовать FSM-граф в .dot format

  3. Akka FSM имеет хорошее свойство позволяет связать некоторые данные с государством, не только давая имя объекта. Это также приветствуется. (Тем не менее, Akka FSM не всегда удобно использовать, поскольку это асинхронный и иногда немного тяжеловесный.)

+0

FSM могут быть красивыми, если они выражаются как взаимно рекурсивные функции. Реальные хвостовые звонки являются ключевыми там, тем не менее, поэтому Scala не сократит его. Чтобы избежать «var», просто верните следующее состояние вместе с сообщениями и продолжайте кормить функцию в себе. Вы эффективно создаете тип «State». –

+0

Структура Akka имеет полезную реализацию конечного автомата, но она скорее зависит от отправки сообщений вокруг системы актеров. Вы можете прочитать больше [здесь] (http://doc.akka.io/docs/akka/2.2.3/scala/fsm.html) –

ответ

7

Это, вероятно, не то, что вы ищете, но я думаю, что это интересная концепция.

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

Использование будет выглядеть так:

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

Ваш случай использования будет сильно определить структуру кода в этой концепции. Вариант использования действительно определяет, какую информацию типа вы хотите сохранить.

Это понятие, потому что состояние сохраняется с использованием системы типов и что во время компиляции сообщается о незаконных переходах.

+1

Интересный подход. Строго типизированные состояния. Тем не менее, я думаю, что подпись Transformation должна быть: case class Transformation [M <: Message, From <: State, To <: State] ( f: M => (To, ResultMessage)) –

+1

@ArseniyZhizhelev Согласен, Я просто последовал твоему примеру. Теоретически вы можете привязать тип вывода к типу сообщения. Действительно зависит от варианта использования, как далеко вы хотите идти с типами в Scala, ха-ха. – EECOLOR

+0

Очень интересно ... но как только я вернусь s1 или что-то еще, как я могу проверить его? Предположим, что такой метод: def typeOf [T: TypeTag] (t: T) = reflect.runtime.universe.typeOf [T], кто я определяю состояние/ответ, возвращаемый TypicalFSM, чтобы я мог принять какое-либо действие? typeOf (s1) соответствует {??? } – j3d