【问题标题】:What is the basic structure in Netwire 5?Netwire 5 的基本结构是什么?
【发布时间】:2014-08-16 09:36:02
【问题描述】:

我正在尝试进入 Netwire,我已经挖掘以查找文档、介绍、教程等等,但几乎每个教程和现有代码对于 Netwire 5 来说都是过时的,并且使用 Netwire 4 中的功能只是不再和我们在一起。 README 有点帮助,但并非所有内容都能编译,而且它仍然几乎没有提供足够的信息来开始使用。

我要求解释或示例只是为了让游戏循环运行并能够响应事件,因此我寻求信息以便最终知道:

  1. 基本结构(例如如何在响应式香蕉中执行使用处理程序、定义行为和对事件做出反应的网络描述)。
  2. 它最终如何进入main
  3. 如何处理 IO 事件(如鼠标单击、按键按下或游戏循环回调),事件如何进入会话等。

还有其他相关的。

我认为从那里我可以运行一些东西,所以我可以通过实验来学习其余的东西(因为在第 5 版中的文档和教程状态非常不存在,我希望很快就会出现一些)。

谢谢!

【问题讨论】:

    标签: haskell functional-programming reactive-programming frp netwire


    【解决方案1】:

    免责声明:我还没有找到任何使用 Netwire 的大型程序,所以我要写的所有内容都应该对你有所保留,因为它是基于我自己使用 Netwire 的经验。我在这里使用的示例大多取自from my own libraryattempt at writing a game using FRP,可能不是做事的“正确方式”。


    问题 1:基本结构(例如如何在响应式香蕉中执行使用处理程序、定义行为并对事件做出反应的网络描述)。

    会话: netwire库的作者给了really good answer关于一个netwire程序的基本结构。由于有点老了,我将在这里概述一些要点。在看wire之前,我们先来看看netwire是如何处理时间的,FRP的底层驱动。在不使用测试工具testWire 的情况下提前时间的唯一方法是生成一个Session,它将有状态地返回时间增量。 Sessions 保存状态的方式封装在它们的类型中:

    newtype Session m s = Session { stepSession :: m (s, Session m s) }
    

    这里,Session 位于 Monad(通常为 IO)内,每次对其求值时,都会返回 s 类型的“时间状态”值和新的 Session。通常,任何有用的状态s 都可以写成Timed 值,它可以返回Real t 的一些实例:

    data Timed t s
    class (Monoid s, Real t) => HasTime t s | s -> t where
        -- | Extract the current time delta.
        dtime :: s -> t
    instance (Monoid s, Real t) => HasTime t (Timed t s)
    

    例如,在游戏中,您通常want a fixed timestep 进行更新调用。 netwire 将此概念编码为:

    countSession_ :: Applicative m => t -> Session m (Timed t ())
    

    countSession_ 将时间步长作为输入,在本例中为t 类型的固定值,并生成Session,其状态值的类型为Timed t ()。这意味着它们只编码t 类型的单个值,并且不携带任何带有() 值的附加状态。在我们讨论了电线之后,我们将看到这在评估电线时起到了怎样的作用。

    电线: Netwire 中“电线”的主要类型是:

    Wire s e m a b
    

    这条线描述了 b 类型的 reactive 值,它执行以下操作:

    • a 类型的反应值作为输入
    • 在 Monad m 内运行
    • 可能抑制或不产生值,产生e 类型的抑制值
    • 假定由s 给出的时间状态

    根据作为反应值的性质,可以将线视为时变函数。因此,每条线都被编码为时间的函数(或时间状态s),在该时刻产生一个b 类型的新值,以及用于评估下一个输入的新线输入a。通过返回一个值和一个新连线,函数可以通过函数定义传播状态来包含状态。

    此外,电线可能抑制或不产生值。这对于未定义计算(例如当鼠标位于应用程序窗口之外)时很有用。这使您可以实现类似switch 之类的东西,其中一条线路更改为另一条线路以继续执行(例如玩家完成他的跳跃)。

    有了这些思路,我们就可以看到netwire中wire的主要驱动:

    stepWire :: Monad m => Wire s e m a b -> s -> Either e a -> m (Either e b, Wire s e m a b)
    

    stepWire wire timestate input 与我们之前所说的完全一样:它接受wire 并将当前的timestateinput 传递给前一条线。然后,在底层的 Monad m 中,它要么产生 Right b 的值,要么以 Left e 的值抑制,然后提供下一条线用于计算。

    问题 2:它最终是如何进入 main 的。

    有了 SessionWire 类型的值,我们可以构造一个循环来一遍又一遍地做两件事:

    1. 步进会话以接收新的时间状态
    2. 使用新的时间状态来步进线

    下面是一个将固定计数器更改为永远以二为单位计数的程序示例:

    import Control.Wire
    
    -- My countLoop operates in the IO monad and takes two parameters:
    --   1. A session that returns time states of type (Timed Int ())
    --   2. A wire that ignores its input and returns an Int
    countLoop :: Session IO (Timed Int ()) -> Wire (Timed Int ()) () IO a Int -> IO ()
    countLoop session wire = do
      (st, nextSession) <- stepSession session
      (Right count, nextWire) <- stepWire wire st (Right undefined)
      print count
      countLoop nextSession nextWire
    
    -- Main just initializes the procedure:
    main :: IO ()
    main = countLoop (countSession_ 1) $ time >>> (mkSF_ (*2))
    

    问题 3:如何处理 IO 事件(如鼠标点击、按键或游戏循环回调),事件如何进入会话等。

    关于如何做到这一点存在一些争论。我认为在这种情况下,最好利用底层的 Monad m,并将当前状态的快照简单地传递给 stepWire 函数。这样做时,我的大多数输入线看起来像这样:

    mousePos :: Wire s e (State Input) a (Float, Float)
    

    忽略连线的输入,从State monad 读取鼠标输入。我使用State 而不是Reader 来正确处理键去抖动(这样单击UI 时不会同时单击UI 下方的某些内容)。状态在我的main 函数中设置并传递给runState,它也执行导线步进。像这样的电线的抑制行为可以编写一些优雅的代码。例如,假设您的箭头键有rightleft 连线,如果按下该键会产生一个值,否则会产生一个值。您可以使用如下所示的线创建角色移动:

    (right >>> moveRight) <|> (left >>> moveLeft) <|> stayPut
    

    由于线是Alternative 的一个实例,如果right 被禁止,它只会移动到下一个可能的线。仅当 ab 都被禁止时,a &lt;|&gt; b 才会禁止。

    您也可以编写代码来利用 netwire 的 Event 系统,但您必须使用 Control.Wire.Unsafe.Event 制作自己的返回 Event 的线路。话虽如此,我还没有发现这种抽象比简单的抑制更有用。

    【讨论】:

    • 如果我们见面我会为你写这个答案买啤酒
    【解决方案2】:

    回答这个问题可能真的太晚了,但我在这里有一个答案,其中包含 Netwire 5 程序的(简约)结构:Console interactivity in Netwire?

    【讨论】:

      猜你喜欢
      • 2011-06-15
      • 2018-01-14
      • 2015-12-21
      • 1970-01-01
      • 2015-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      相关资源
      最近更新 更多