PureScript 函数在运行时给出错误的类型

PureScript functions giving wrong type at runtime

这是我用 PureScript 编写的函数。它编译时没有错误或警告。该函数做了一些设置来处理鼠标移动:

startMouseHandlers :: forall h e. STRef h {x::Number, y::Number}
  -> STRef h {x::Number, y::Number}
  -> Eff (dom :: DOM, st :: ST h | e) Unit
startMouseHandlers angleRef velocityRef = do
  lastMousePos <- newSTRef {x: 0.0, y: 0.0}
  body <- JQuery.body
  let
    moveHandler event jq = do
    x <- getPageX event
    y <- getPageY event
    lastPos <- readSTRef lastMousePos
    angle <- readSTRef angleRef
    let newAngle = {
          x: angle.x + x - lastPos.x,
          y: angle.y + y - lastPos.y
        }
    rotateCube angleRef newAngle
    void $ writeSTRef lastMousePos {x: x, y: y}
  on "mousemove" movehandler body

angleRefvelocityRef 是引用可变变量的 STRef 变量。它们由调用函数传入。

下面是同一代码片段的编译 JavaScript 版本:

var startMouseHandlers = function (angleRef) {
      return function (velocityRef) {
      return function __do() {
          var v = Control_Monad_ST.newSTRef({
              x: 0.0, 
              y: 0.0
          })();
          var v1 = Control_Monad_Eff_JQuery.body();
          var moveHandler = function (event) {
              return function (jq) {
                  return function __do() {
                      var v2 = Control_Monad_Eff_JQuery.getPageX(event)();
                      var v3 = Control_Monad_Eff_JQuery.getPageY(event)();
                      var v4 = Control_Monad_ST.readSTRef(v)();
                      var v5 = Control_Monad_ST.readSTRef(angleRef)();
                      var newAngle = {
                          x: (v5.x + v2) - v4.x, 
                          y: (v5.y + v3) - v4.y
                      };
                      rotateCube(angleRef)(newAngle)();
                      return Data_Functor["void"](Control_Monad_Eff.functorEff)(Control_Monad_ST.writeSTRef(v)({
                          x: v2, 
                          y: v3
                      }))();
                  };
              };
          };
          return Control_Monad_Eff_JQuery.on("mousemove")(moveHandler)(v1)();
      };
      };
  };

这就是我面临的问题:这段代码的重点是使用从鼠标移动中获得并存储在 newAngle 中的新计算角度调用 rotateCube 函数。

但是,当我 运行 在浏览器中使用这段代码时,它并没有按照我的意愿执行。然后我尝试调试 JavaScript 代码,我发现 newAngle 变量最终的值为

{ x: NaN, y: NaN }

跟踪代码后我发现原因是因为变量v5.x(在PureScript中是angle.x)有一个Number类型的值,但是变量v2v4.xxlastPos.x)具有以下类型

v2: Object[1]
  0: 127
  length: 1

而不是 Number。同样的事情发生在 ylastPos.y 的情况下。我猜测将此与 angle.xNumber 类型相加会导致 NaN。但为什么会这样呢?为什么 xlastPos.x 在 javascript 的 运行 时间不是 Number 而是看起来像这些数组?

我不确定这是否是一个线索,但是 Control.Monad.Eff.JQuery 中的 on 函数将一个函数作为参数(在本例中为 moveHandler)将在事件发生时执行,该函数的类型应为

(JQueryEvent -> JQuery -> Eff (dom :: DOM | eff) a))

但是在moveHandler里面我有两个副作用,一个是DOM,另一个是ST因为使用了readSTRef.

我实际上尝试在 moveHandler 的定义之前显式声明其类型

moveHandler :: forall e h. JQueryEvent -> JQuery -> Eff (dom :: DOM, st :: ST h | eff) Unit))

但这总是会给我一个 Could not match type 错误,这就是为什么我最终没有提到 moveHandler 的显式类型的原因。这可能是代码不起作用的原因吗?在事件处理程序回调函数中不可能也发生 ST 效果吗?

正如 Phill Freeman 和 Yury Tarabanko 在评论中所建议的那样,事实证明 purescript-jquery 中的 getPageX/Y 是 return 错误的类型。现在已更正,更新包后,程序按原样执行并且 newAngle 具有正确的值。

尽管 getPageX/Y 的 return 类型不正确解释了为什么 xy 不正确,但我仍然对为什么 lastPos.xlastPos.y 也是不正确的类型,即使它们是由 readSTRef 分配的。直到现在我才意识到我愚蠢的大脑没有考虑清楚。 lastPos 存储最后一个鼠标位置,该位置是 getPageX/Y 获得的。所以这两种奇怪的类型都是由同一个错误引起的。