纯功能反馈抑制?

时间:2014-06-26 13:24:54

标签: functional-programming state stateless

我有一个问题,我可以通过使用状态的经典命令式编程轻松解决:我正在编写一个在多个节点之间共享URL的共同浏览应用程序。该程序有一个用于通信的模块,我称之为link,用于浏览器处理,我称之为browser。现在当一个URL到达link时,我使用browser模块来告诉 实际的Web浏览器开始加载URL。

实际的浏览器将触发传入URL已开始加载的导航检测,因此将立即显示为发送到另一方的候选者。必须避免这种情况,因为它会沿着以下(非常概念化的)伪代码(它的Javascript,但是请考虑一个有点无关的实现)创建一个无限循环的链接跟随到同一个URL。细节):

actualWebBrowser.urlListen.gotURL(function(url) {
  // Browser delivered an URL
  browser.process(url);
});
link.receivedAnURL(function(url) {
  actualWebBrowser.loadURL(url); // will eventually trigger above listener
});

我首先要做的是将每个传入的网址存储在browser中,并在网址到达时立即使用该网址,然后将其从收到的网址中删除。在browser中列出了这一行:

browser.recents = {} // <--- mutable state
browser.recentsExpiry = 40000;

browser.doSend = function(url) {
  now = (new Date).getTime();
  link.sendURL(url); // <-- URL goes out on the network

  // Side-effect, mutating module state, clumsy clean up mechanism :(
  browser.recents[url] = now;
  setTimeout(function() { delete browser.recents[url] }, browser.recentsExpiry);

  return true;
}
browser.process = function(url) {
  if(/* sanity checks on `url`*/) {
    now = (new Date).getTime();
    var duplicate = browser.recents[url];
    if(! duplicate) return browser.doSend(url);
    if((now - duplicate_t) > browser.recentsExpiry) {
      return browser.doSend(url);
    }
    return false;
  }
}

它有效但我对我的解决方案感到有些失望,因为我在browser习惯性地使用了可变状态。是否有更好的方式(tm)&#34;在这种情况下使用不可变数据结构/函数式编程等?

1 个答案:

答案 0 :(得分:0)

处理长寿状态的一种更实用的方法是将它用作递归函数的参数,并且有一个函数执行负责处理某种类型的单个“动作”,然后再用它来调用它自己。新州。

F#的MailboxProcessor就是这种方法的一个例子。但是它确实依赖于在独立线程上进行处理,这与代码的事件驱动样式不同。

正如您所知,代码中的setTimeout使状态管理变得复杂。您可以简化此操作的一种方法是让browser.process过滤掉任何超时的网址,然后再执行其他操作。这也将消除对正在处理的特定URL的额外超时检查的需要。

即使您无法完全消除代码中的可变状态,也应该仔细考虑该状态的范围生存期

例如,您可能需要多个独立浏览器吗?如果是这样,您应该考虑如何将recents集合封装为仅属于单个浏览器,这样您就不会发生冲突。即使您不需要多个实际应用程序,这可能有助于测试。

您可以通过多种方式将状态保持为特定浏览器的私有状态,具体取决于该语言的可用功能。例如,在具有对象的语言中,自然的方式是使其成为浏览器对象的私有成员。