如何以纯FP方式设计有状态的参与者

时间:2018-06-26 10:24:02

标签: scala akka

我有一个任务:在消息“ userStart”上启动计时器5秒钟,如果用户将在计时器之前发送答案->取消计时器。代码简单易行,问题是如何以FP方式进行编码。据我了解,我应该使用“ val”而不是“ var”。我是FP的新手,所以很高兴有人可以帮助我解决这个问题,或者推荐一些资料来源,在这些资料中我可以找到简单的示例来做这些事情。谢谢!

#include <iostream>
#include <list>
using namespace std;

class Instrument {
    public:
        virtual void play() {
            cout << "..." << endl;;
        }
};

class ElectricGuitar: public Instrument {
    public:
        void play() {
            cout << "Djent" << endl;
        }
};

class Piano: public Instrument {
    public:
        void play() {
            cout << "Pling" << endl;
        }
};

void pointers() {
    cout << "Pointers:" << endl;
    Instrument *g = new ElectricGuitar();
    Instrument *p = new Piano();
    g->play(); // Djent
    p->play(); // Pling
    cout << endl;
}

void references() {
    cout << "References:" << endl;
    ElectricGuitar g;
    Piano p;
    Instrument &ig = g;
    Instrument &ip = p;
    ig.play();
    ip.play();
    cout << endl;
}

void list_bad() {
    cout << "Bad list:" << endl;
    list<Instrument> instruments;
    instruments.push_back(ElectricGuitar());
    instruments.push_back(Piano());
    for (auto i: instruments) {
        i.play();
    }
    cout << endl;
}

void list_good() {
    cout << "Good list:" << endl;
    list<Instrument *> instruments;
    instruments.push_back(new ElectricGuitar());
    instruments.push_back(new Piano());
    for (auto *i: instruments) {
        i->play();
    }
    cout << endl;
}
int main(void) {
    pointers();
    references();
    list_bad();
    list_good();
    return 0;
}

2 个答案:

答案 0 :(得分:2)

以下是使用become来跟踪接收函数中状态的版本:

class Game extends Actor{     
  def startTimer(): Cancellable = context.system.scheduler.scheduleOnce(5 seconds, self, "userMissed")

  def receive = idleReceive(0)

  def idleReceive(actsCount: Int): Actor.Receive = {
    case "userStart" => startTimer()
      context.become(waitingReceive(actsCount, startTimer()))
      sender() ! "do move"
  }

  def waitingReceive(actsCount: Int, timer: Cancellable): Actor.Receive = {
    case "userAct" =>
      println("> user made his move")
      context.become(idleReceive(actsCount + 1))
      timer.cancel()
    case "userMissed" =>
      println("> user missed his move")
      context.become(idleReceive(actsCount))
  }
}

需要更多的错误处理,包括在传递“ userAct”消息时计时器触发的竞赛条件。您还需要添加另一条消息来检索actsCount的值。

答案 1 :(得分:2)

如评论中所述:您的特定用例从本质上讲是全状态的,并且已经封装在Actor中,从而可以摆脱纯粹的功能范式。

但是,如果您仍然对设计不满意,则可以选择以下几种方法:

有限状态机

Akka提供了一些机制,使Actor的行为类似于FSM。实际上,the example in the documentation看起来很像您的演员。

成为/不成为

演员还具有根据传入的消息change their receive method的能力。您的计时逻辑可以嵌入到变得&变得不受欢迎的逻辑中。