1

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

我正在研究 Signals/Effects 并且一直在研究 the-elm-architecture,特别是example 8

在这个例子中(据我所知),如果你点击形状:

  • 信号消息与操作一起发送到地址
  • SPIN操作在更新时触发
  • 如果他的形状当前没有动画,也会调用 Tick。

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

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

你会在我的评论中看到我可以达到我的Punch行动,但我似乎从来没有达到过Tick

我看过这个问题,但是因为我使用的是键盘信号而不是榆树 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中。

4

1 回答 1

2

Tick操作永远不会被触发,因为没有端口传输EffectsTasks. 在您发布的相关 Stack Overflow 答案中,StartApp正在使用,因此连接端口很容易。

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

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

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

请注意,此邮箱是根据操作列表定义的。这是将效果转换为任务的复杂部分之一,在此处进行了解释

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

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

该端口现在会将您的Tick操作发送到邮箱,但我们还没有连接您的模型来监听键盘空格键和这个新邮箱。为此,我们将使用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. 查看文档,很明显我们只是将第一个元素从列表中删除是安全的。

所以现在我们有了一个由键盘空格键和 Tick 动作触发的信号。最后一部分是让model听众听到那个信号,而不仅仅是键盘。

model : Signal ( Model, Effects Action )
model =
  Signal.foldp update initialModel signals
于 2016-03-05T11:24:45.377 回答