无法到达榆树 Tick 动作

Cannot reach elm Tick action

我希望有人可以在信号和效果方面帮助我。

我正在研究 Signals/Effects 并且一直在研究榆树架构,尤其是 example 8

在此示例中(据我了解),如果您单击形状:

我正在尝试复制完全相同的流程(使用类似的代码),但我不想点击按钮并使用 HTML 包,我只想使用空格键发送信号。

在我的代码中,空格键向 Punch 操作发送一个信号。就像示例 8 一样,我还想调用 Tick 并更新模型中的 debounceState

您会在我的评论中看到我可以达到我的 Punch 操作,但我似乎永远无法达到 Tick

我看过 ,但因为我使用的是键盘信号而不是 elm StartApp,所以我认为它不适用。

如果有人能解释为什么我无法在示例中达到 Tick,我将不胜感激。

module Main (..) where

import Graphics.Element exposing (..)
import Time exposing (Time, second)
import Effects exposing (Effects)
import Keyboard


type alias DebounceState =
  Maybe
    { prevClockTime : Time
    , elapsedTime : Time
    }


type alias Model =
  { punching : Bool
  , count : Int
  , randomString: String
  , debounceState : DebounceState
  }


duration = second


-- MODEL


initialModel : ( Model, Effects Action )
initialModel =
  ( { punching = False
    , count = 0
    , randomString = ""
    , debounceState = Nothing
    }
  , Effects.none
  )


model : Signal ( Model, Effects Action )
model =
  Signal.foldp update initialModel (punch Keyboard.space)


-- ACTIONS


type Action
  = NoOp
  | Punch Bool
  | Tick Time


-- UPDATE


update : Action -> ( Model, Effects Action ) -> ( Model, Effects Action )
update action ( model, fx ) =
  case action of
    NoOp ->
      ( model, Effects.none )

    Punch isPunching ->
      case model.debounceState of
        Nothing ->
          ( { model |
              punching = isPunching
              , count = model.count + 1 }, Effects.tick Tick )

        Just _ ->
          ( { model |
              punching = isPunching
              , count = model.count + 2 }, Effects.none )

    -- I don't seem to reach `Tick` at all
    -- You'll notice that I try to apply a value to `randomString` in the
    -- conditional to indicate where I end up
    Tick clockTime ->
      let
        newElapsedTime =
          case model.debounceState of
            Nothing ->
              0

            Just {elapsedTime, prevClockTime} ->
              elapsedTime + (clockTime - prevClockTime)
      in
        if newElapsedTime > duration then
          ( {model | randomString = "foo"} , Effects.none )
        else
          ( {model | randomString = "bar"} , Effects.tick Tick )


-- SIGNAL


punch : Signal Bool -> Signal Action
punch input =
  Signal.map Punch input


-- VIEW

view : ( Model, Effects Action ) -> Element
view model =
  show model


main : Signal Element
main =
  Signal.map view model

直接粘贴到 Try Elm

Tick 操作从未​​触发,因为没有端口将 Effects 传输到 Tasks。在您发布的相关 Stack Overflow 答案中,正在使用 StartApp,因此连接端口很容易。

由于您没有使用 StartApp,因此您需要手动执行一些操作。现在,您的 model 只听到键盘 space 信号。您需要连接信号处理,以便您的效果一直返回到触发您的 update 功能。

您需要一个邮箱来协调信号。

actions : Signal.Mailbox (List Action)
actions =
  Signal.mailbox []

请注意,此邮箱是根据操作列表定义的。这是将 Effects 转换为 Tasks 的复杂部分之一,它是 explained here.

现在您需要一个端口来获取 (Model, Effects Action) 元组的第二部分并将其转换为 Task.

port effects : Signal (Task.Task Effects.Never ())
port effects =
  Signal.map (Effects.toTask actions.address << snd) model

该端口现在会将您的 Tick 操作发送到邮箱,但我们仍未连接您的模型以同时收听键盘 space 键和这个新邮箱。为此,我们将使用 Signal.mergeMany.

signals : Signal Action
signals =
  let
    singleAction list =
      case list of
        [] -> NoOp
        (a::_) -> a
  in
    Signal.mergeMany
      [ punch Keyboard.space
      , Signal.map singleAction actions.signal
      ]

singleAction 函数中发生的有趣事情只是让我们了解了这样一个事实,即 Effects.toTask 迫使我们使用 Actions 的列表而不是单个 Action.查看文档,很明显我们可以安全地从列表中删除第一个元素。

现在我们有了一个由键盘 space 栏和 Tick 动作触发的信号。最后一部分是让 model 收听该信号而不仅仅是键盘。

model : Signal ( Model, Effects Action )
model =
  Signal.foldp update initialModel signals