Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 在函数式反应式编程中,如何在应用程序的两个部分之间共享状态?_Haskell_Functional Programming_Frp - Fatal编程技术网

Haskell 在函数式反应式编程中,如何在应用程序的两个部分之间共享状态?

Haskell 在函数式反应式编程中,如何在应用程序的两个部分之间共享状态?,haskell,functional-programming,frp,Haskell,Functional Programming,Frp,我有一些应用程序架构,其中用户输入流到一些自动机,自动机在事件流的上下文中运行,并将用户引导到应用程序的不同部分。应用程序的每个部分都可以根据用户输入运行一些操作。但是,应用程序的两个部分共享某些状态,并且从概念上讲,读写状态相同。需要注意的是,这两个“线程”不是同时运行的,其中一个“暂停”,而另一个“产生”输出。在不使用全局变量的情况下,描述这种状态共享计算的规范方法是什么?这两个“线程”保持通过某种形式的消息传递同步的本地状态有意义吗,即使它们无论如何都不是并发的 没有代码示例,因为问题更具

我有一些应用程序架构,其中用户输入流到一些自动机,自动机在事件流的上下文中运行,并将用户引导到应用程序的不同部分。应用程序的每个部分都可以根据用户输入运行一些操作。但是,应用程序的两个部分共享某些状态,并且从概念上讲,读写状态相同。需要注意的是,这两个“线程”不是同时运行的,其中一个“暂停”,而另一个“产生”输出。在不使用全局变量的情况下,描述这种状态共享计算的规范方法是什么?这两个“线程”保持通过某种形式的消息传递同步的本地状态有意义吗,即使它们无论如何都不是并发的


没有代码示例,因为问题更具概念性,但欢迎使用Haskell(使用任何FRP框架)或其他语言的示例进行回答。

我一直在研究解决此问题的方法。高级总结是您:

A) 将所有并发代码提取为纯单线程规范

B) 单线程规范使用
StateT
共享公共状态

整体架构的灵感来自模型视图控制器。你有:

  • 控制器,它们是有效的输入
  • 视图,它们是有效的输出
  • 一个模型,它是一个纯流转换
模型只能与一个控制器和一个视图交互。但是,控制器和视图都是幺半群,因此可以将多个控制器合并到单个控制器中,将多个视图合并到单个视图中。从图表上看,它如下所示:

 controller1 -                                           -> view1
              \                                         /
 controller2 ---> controllerTotal -> model -> viewTotal---> view2
              /                                         \
 controller3 -                                           -> view3

                  \______ ______/   \__ __/   \___ ___/
                         v             v          v
                     Effectful        Pure    Effectful
>>> quickCheck $ \n -> length ((`evalState` initialStatus) $ P.toListM $ each (replicate n (Key 'x')) >-> runEdge (rcplCore t)) == n || n < 0
该模型是一个纯单线程流转换器,它实现了
箭头
箭头选择
。原因是:

  • 箭头
    是与并行性等效的单线程
  • ArrowChoice
    是单线程的并发等价物
在本例中,我使用基于推送的
管道
,它似乎有一个正确的
箭头
箭头选择
实例,尽管我仍在验证这些定律,所以在我完成它们的证明之前,这个解决方案仍然是实验性的。对于好奇的人,相关类型和实例如下:

newtype Edge m r a b = Edge { unEdge :: a -> Pipe a b m r }

instance (Monad m) => Category (Edge m r) where
    id = Edge push
    (Edge p2) . (Edge p1) = Edge (p1 >~> p2)

instance (Monad m) => Arrow (Edge m r) where
    arr f = Edge (push />/ respond . f)
    first (Edge p) = Edge $ \(b, d) ->
        evalStateP d $ (up \>\ unsafeHoist lift . p />/ dn) b
      where
        up () = do
            (b, d) <- request ()
            lift $ put d
            return b
        dn c = do
            d <- lift get
            respond (c, d)

instance (Monad m) => ArrowChoice (Edge m r) where
    left (Edge k) = Edge (bef >=> (up \>\ (k />/ dn)))
      where
          bef x = case x of
              Left b -> return b
              Right d -> do
                  _ <- respond (Right d)
                  x2 <- request ()
                  bef x2
          up () = do
              x <- request ()
              bef x
          dn c = respond (Left c)
n
是我按
x
键的次数。运行该测试将产生以下输出:

*** Failed! Falsifiable (after 17 tests and 6 shrinks):
78
快速检查发现我的属性是假的!此外,由于代码是引用透明的,QuickCheck可以将反例缩小到最小的复制冲突。按下78键后,终端驱动程序会发出一个换行符,因为控制台宽80个字符,提示会占用两个字符(
“>”
)。如果并发性和
IO
感染了我的整个系统,我将很难验证这种属性

有一个纯粹的设置是伟大的另一个原因:一切都是完全可复制的!如果我存储了所有传入事件的日志,那么任何时候出现错误时,我都可以重播这些事件,并且可以将完全复制的测试用例添加到我的测试套件中

然而,纯度最重要的好处是能够更轻松地对代码进行非正式和正式的推理。当您从等式中删除Haskell的调度器时,您可以静态地证明代码的某些方面,而当您必须依赖具有非正式指定语义的并发运行时,您无法证明这些方面。事实证明,即使对于非正式的推理,这也是非常有用的,因为当我将代码转换为使用
mvc
时,它仍然有几个bug,但这些bug比我第一次迭代中顽固的并发bug更容易调试和删除


rcpl
示例使用
StateT
在不同组件之间共享全局状态,因此对您的问题的冗长回答是:您可以使用
StateT
,但前提是您将系统转换为单线程版本。幸运的是这是可能的

我认为这个问题太宽泛,无法给出具体的答案。您建议的任何策略(同步、FRP、全局VAR)都可能适合给定的情况。或者可能是本地共享的
IORef
MVar
。或者,如果计算实际上是在单个线程中,则使用
StateT
monad转换器。我不清楚
“threads”
是指由
forkIO
创建的实际线程,还是严格意义上的概念性线程,而您实际上只运行一个线程。@JohnL:既然这个问题提到了FRP,我想一个关于如何通过应用程序共享行为或事件流的答案会很好。我认为将一个行为(或多个行为,如果合适的话)贯穿应用程序大体上是一个不错的选择,但在将其转化为答案之前,我确实需要解决细节问题。也许几个小时后没人来……很抱歉,但是型号和类型
Edge
之间有什么联系?整个MVC图是一个
Edge
吗?是的,
Edge
应该被称为
Model
。这些名字仍在不断变化。而且,是的,整个MVC图只是一个大的
边缘。