Reverse State monad的现实生活和有用的例子

时间:2017-04-30 23:36:13

标签: haskell lazy-evaluation state-monad

Reverse State monad 非常好用,是Haskell语言的表现力和懒惰评价的典范。但要理解这个单子并不容易。此外,与使用该语言中的任何其他工具相比,使用 Reverse State monad 更容易找到一些令人信服的现实生活示例。

反向状态 monad的定义方式如下:

newtype RState s a = RState { runRState :: s -> (a,s) }

instance Monad (RState s) where
    return x = RState $ (,) x
    RState sf >>= f = RState $ \s ->
        let (a, past)   = sf future
            (b, future) = runRState (f a) s
        in (b, past)

它已经有一些例子和用法,但我发现它们并不实用。

  1. Quora answer:很好地解释,甚至有现实生活中的使用示例但没有代码,并且不清楚使用RState是否真的好主意。
  2. Mindfuck:介绍这个好概念,但是示例没用。没有人会这样写斐波那契数字。
  3. Kwang's Haskell Blog:显示Writer如何使用RState进行模拟,但是请继续。不是真实的例子:)
  4. 我也知道tardis包但没有这个库的教程,文档示例真的是抽象的,没有那么多人真正理解它。最接近我想要的是this tutorial,但它有tardis的示例,而不仅仅是RState。以及this book reference

    因此,我不是在寻找tardis现实生活模式,如果可能,我只对RState插图感兴趣。虽然我知道可能没有纯RState用法的样本。在这种情况下,使用RStateT转换器或tardis的最小示例就足够了。

    有人在现实生活中使用过这个monad还是真的很好看&有用的插图与代码?

1 个答案:

答案 0 :(得分:5)

我已经知道这些单子了十多年了,直到最近才看到它们的实际应用。这是一个不寻常的设置。我和一个同事正在通过“反射”库使用功能性反应式编程,并且正在使用一个库来帮助构建终端图形应用程序。如果您熟悉'reflex-dom',它的性质类似,除了我们的基本monad而不是将后续的小部件一个接一个地放置在DOM中,而只是将基于终端字符单元的“图像”堆叠在顶部彼此之间的区别,取决于用户是否合理地切割屏幕。我们想提供比这更好的东西,它可以在某种程度上跟踪剩余的屏幕房地产,并让用户在行和列中放置一些“平铺”,这样一个do-block基本上对应于一个屏幕上的一排或一排瓷砖。

除了处理布局问题外,我们还希望这些图块能够管理键盘焦点,从而允许用户按Tab键在它们之间循环显示,或者使Tab键反向显示。正是在这里,时间向前和向后状态monad转换器变得非常方便:我们可以使任一方向上的当前状态成为一个事件(一个空元组)。每个磁贴都可以将事件发送到上一个和下一个小部件(并从中接收事件),在它们收到键盘焦点时通知小部件,因此应停止阻止按键到达其子小部件。如此示意,图块小部件看起来像:

do rec focusP <- recvFromPast
       sendToPast shiftTabPress
       tabPress <- gate focused $ ... filter input Event for Tab keypresses ...
       shiftTabPress <- gate focused $ ... filter input Event for Shift-Tab ...
       focused <- hold False $ leftmost
         [ True <$ (focusP <> focusF)
         , False <$ (shiftTabPress <> tabPress) ]
       v <- ... run the child widget and do layout stuff ...
       sendToFuture tabPress
       focusF <- recvFromFuture
   return v

在这里,sendToFuture是普通状态“ put”,sendToPast是反向时间“ put”,recvFromPast是普通状态“ get”,而recvFromFuture是反向时间“ get”。因此,focusP :: Event t ()是我们从前任(可能是另一个这样的活动)获得的事件,它告诉我们我们现在有重点,而focusF是我们从后任获得的类似事件。我们使用'hold'构造focused :: Behavior t Bool来跟踪焦点,然后将其用于门控键盘事件,以便确保告诉邻居我们只有在我们自己的情况下才会得到焦点集中精力,也用于我在运行子窗口小部件时所忽略的位置,以便适当地过滤其输入事件。

我不确定在发布库时我们实际上是否仍会以这种方式进行操作,但是到目前为止它似乎运行良好,我很高兴终于注意到这种情况。施工可以投入实际使用。