gpt4 book ai didi

dom - (PureScript) 如何在 Eff 以外的单子(monad)上下文中运行 DOM 事件监听器回调?

转载 作者:行者123 更新时间:2023-12-02 07:53:59 25 4
gpt4 key购买 nike

我正在使用 PureScript 制作 Canvas 游戏,我想知道处理事件监听器的最佳方法是什么,特别是在自定义 monad 堆栈中运行回调。这是我的游戏堆栈...

type BaseEffect e = Eff (canvas :: CANVAS, dom :: DOM, console :: CONSOLE | e)
type GameState = { canvas :: CanvasElement, context :: Context2D, angle :: Number }
type GameEffect e a = StateT GameState (BaseEffect e) a

我想做的是在按下任意键时更改 GameState 中的“角度”属性(仅出于开发目的,以便我可以调整图形)。这是我的回调函数...

changeState :: forall e. Event -> GameEffect e Unit
changeState a = do
modify \s -> s { angle = s.angle + 1.0 }
liftEff $ log "keypress"
pure unit

但是 addEventListenereventListener看起来它们只能与 Eff 一起使用,因此以下内容不会键入检查...

addEventListener
(EventType "keypress")
(eventListener changeState)
false
((elementToEventTarget <<< htmlElementToElement) bodyHtmlElement)

我想我可以自己定义 addEventListener 和 eventListener使用外部函数接口(interface)导入它们(将 Eff 更改为 GameEffect)。该类型已检查,但当我尝试在浏览器中运行时导致控制台错误。

foreign import addEventListener :: forall e. EventType -> EventListener e -> Boolean -> EventTarget -> GameEffect e Unit
foreign import eventListener :: forall e. (Event -> GameEffect e Unit) -> EventListener e

处理 monad 堆栈中正在运行的回调的最佳方法是什么?

最佳答案

我会使用purescript-aff-coroutines为了这。这意味着将 BaseEffect 更改为 Aff,但任何 Eff 能做的事情 Aff 也能做:

import Prelude

import Control.Coroutine as CR
import Control.Coroutine.Aff as CRA
import Control.Monad.Aff (Aff)
import Control.Monad.Aff.AVar (AVAR)
import Control.Monad.Eff.Class (liftEff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Rec.Class (forever)
import Control.Monad.State (StateT, lift, modify)
import Data.Either (Either(..))

import DOM (DOM)
import DOM.Event.EventTarget (addEventListener, eventListener)
import DOM.Event.Types (Event, EventTarget, EventType(..))
import DOM.HTML.Types (HTMLElement, htmlElementToElement)
import DOM.Node.Types (elementToEventTarget)

import Graphics.Canvas (CANVAS, CanvasElement, Context2D)

type BaseEffect e = Aff (canvas :: CANVAS, dom :: DOM, console :: CONSOLE, avar :: AVAR | e)
type GameState = { canvas :: CanvasElement, context :: Context2D, angle :: Number }
type GameEffect e = StateT GameState (BaseEffect e)

changeState :: forall e. Event -> GameEffect e Unit
changeState a = do
modify \s -> s { angle = s.angle + 1.0 }
liftEff $ log "keypress"
pure unit

eventProducer :: forall e. EventType -> EventTarget -> CR.Producer Event (GameEffect e) Unit
eventProducer eventType target =
CRA.produce' \emit ->
addEventListener eventType (eventListener (emit <<< Left)) false target

setupListener :: forall e. HTMLElement -> GameEffect e Unit
setupListener bodyHtmlElement = CR.runProcess $ consumer `CR.pullFrom` producer
where
producer :: CR.Producer Event (GameEffect e) Unit
producer =
eventProducer
(EventType "keypress")
((elementToEventTarget <<< htmlElementToElement) bodyHtmlElement)
consumer :: CR.Consumer Event (GameEffect e) Unit
consumer = forever $ lift <<< changeState =<< CR.await

因此,这里的 eventProducer 函数为事件监听器创建一个协程生成器,然后 setupListener 执行与理论上的 addEventListener 等效的操作你上面的用法。

其工作原理是为监听器创建一个生产者,然后将其连接到接收到 Event 时调用 changeState 的消费者。协程进程在单子(monad)上下文中运行,这里是您的 GameEffect 单子(monad),这就是一切顺利的原因。

关于dom - (PureScript) 如何在 Eff 以外的单子(monad)上下文中运行 DOM 事件监听器回调?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42080807/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com