【问题标题】:EscapedSkolem error implementing websocket reconnection in PureScript and Halogen在 PureScript 和 Halogen 中实现 websocket 重新连接的 EscapedSkolem 错误
【发布时间】:2018-03-07 02:11:45
【问题描述】:

我正在尝试在 PureScript 中实现 Websocket 重新连接,并且完全不知道如何继续。由于使用了Aff,我在顶层添加了重新连接代码;我认为这是正确的地方,但我不确定。

我尝试在 Haskell 中实现它,但由于 runWs 中的 EscapedSkolem 错误,我无法对其进行类型检查。我的印象是我可以通过添加类型签名来解决这个问题,但我一生都无法弄清楚签名可能是什么!

所以我有三个问题:

  • 这是实现重新连接的正确方法吗?
  • runWs 的类型是什么(任何关于我如何为自己解决这个问题的提示都会很棒)?
  • 如果添加类型签名不能修复 EscapedSkolem 错误,我将如何修复它?

最后,我是 PureScript 的新手,所以如果有任何不清楚的地方,请指出,我会尽力澄清。

编辑:添加了错误编译器输出并稍微更改了标题。

module Main where

import Prelude

import Control.Coroutine (Producer, Consumer, runProcess, consumer, ($$))
import Control.Coroutine.Aff (produce)

import Control.Monad.Aff (Aff, delay)
import Control.Monad.Aff.AVar (AVAR)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Eff.Exception (EXCEPTION)
import Control.Monad.Eff.Ref (REF)
import Control.Monad.Eff.Var (($=), get)

import DOM (DOM)
import DOM.Websocket.Event.CloseEvent (reason)


import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Data.Time.Duration (Milliseconds(..))
import Halogen as H
import Halogen.Aff (HalogenEffects, awaitBody, runHalogenAff)
import Halogen.VDom.Driver (runUI)
import Log (Query(..), component)
import WebSocket (Connection(..), URL(..), WEBSOCKET, newWebSocket, runMessage, runURL, runMessageEvent)


wsURI :: URL
wsURI = URL "ws://localhost:6385"


reconnectionDelay :: Milliseconds
reconnectionDelay = Milliseconds 10000.0


main :: forall eff. Eff (HalogenEffects (console :: CONSOLE, err :: EXCEPTION , avar :: AVAR , dom :: DOM , exception :: EXCEPTION , ref :: REF , ws :: WEBSOCKET | eff)) Unit
main = do
  runHalogenAff do
    body <- awaitBody
    driver <- runUI component unit body

    ---> Replace this: <---
    runProcess (wsProducer $$ wsConsumer driver.query)
    ---> with this: <---
    --   runWs driver

-- -------------------------------------------------
-- -------------------------------------------------
--
-- Reconnection function
-- runWs :: ????????
runWs p = go
  where
    go = do
      runProcess (wsProducer $$ wsConsumer p)
      delay reconnectionDelay
      go

-- -------------------------------------------------
-- -------------------------------------------------


wsProducer :: forall eff. Producer String (Aff (console :: CONSOLE, err :: EXCEPTION , ws :: WEBSOCKET , avar :: AVAR | eff)) Unit
wsProducer = produce \emit -> do

  Connection socket <- newWebSocket wsURI []

  socket.onopen $= \event -> do
    log "onopen: Connection opened"
    log <<< runURL =<< get socket.url


  socket.onmessage $= \event -> do
    emit $ Left $ runMessage (runMessageEvent event)


  socket.onclose $= \event -> do
    log $ "Socket Closed, returning to runHalogenAff: "  <> reason event
    emit $ Right unit


  socket.onerror $= \event -> do
    log "Error."
    emit $ Right unit


wsConsumer :: forall eff . (Query ~> Aff (HalogenEffects eff)) -> Consumer String (Aff (HalogenEffects eff)) Unit
wsConsumer driver = consumer \msg -> do
  driver $ H.action $ AddMessage msg
  pure Nothing

编译器输出为:

Compiling Main
[1/1 MissingTypeDeclaration] src/Main.purs:54:1

      v
  54  runWs p = go
  55    where
  56      go = do
  57        runProcess (wsProducer $$ wsConsumer p)
  58        delay reconnectionDelay
  59        go
            ^

  No type declaration was provided for the top-level declaration of runWs.
  It is good practice to provide type declarations as a form of documentation.
  The inferred type of runWs was:

    forall t110 t120.
      (Query a0
      -> Aff
            ( avar :: AVAR
            , ref :: REF
            , exception :: EXCEPTION
            , dom :: DOM
            , console :: CONSOLE
            , err :: EXCEPTION
            , ws :: WEBSOCKET
            | t120
            )
            a0
      )
      -> Aff
          ( console :: CONSOLE
          , err :: EXCEPTION
          , ws :: WEBSOCKET
          , avar :: AVAR
          , dom :: DOM
          , exception :: EXCEPTION
          , ref :: REF
          | t120
          )
          t110

  where a0 is a rigid type variable
          bound at line 57, column 44 - line 57, column 45

[1/1 EscapedSkolem] src/Main.purs:54:1

      v
  54  runWs p = go
  55    where
  56      go = do
  57        runProcess (wsProducer $$ wsConsumer p)
  58        delay reconnectionDelay
  59        go
            ^

  The type variable a, bound at

    /home/rgh/dev/purescript/translate/sidebar/src/Main.purs line 57, column 44 - line 57, column 45

  has escaped its scope, appearing in the type

    (Query a2
    -> Aff
          ( avar :: AVAR
          , ref :: REF
          , exception :: EXCEPTION
          , dom :: DOM
          , console :: CONSOLE
          , err :: EXCEPTION
          , ws :: WEBSOCKET
          | t120
          )
          a2
    )
    -> Aff
        ( console :: CONSOLE
        , err :: EXCEPTION
        , ws :: WEBSOCKET
        , avar :: AVAR
        , dom :: DOM
        , exception :: EXCEPTION
        , ref :: REF
        | t120
        )
        t110

  in the expression \p ->
                      let
                        go = ...
                      in go
  in value declaration runWs

          Src   Lib   All
Warnings   1     0     1
Errors     1     0     1
* Failed to rebuild; try to fix the compile errors

【问题讨论】:

    标签: websocket purescript halogen


    【解决方案1】:

    编译器错误消息有时可能难以解密,但在这种情况下,它就是您正在寻找的答案。让我们在这里看看你的 do 块:

    do
      runHalogenAff do
        body <- awaitBody
        driver <- runUI component unit body
        runWs driver.query -- < assuming you made a small mistake here
    

    我通常从脱糖开始,我发现它更容易跟踪类型,但是 ymmv:

    runHalogenAff $
      awaitBody >>= \body ->
        runUI component unit body >>= \driver ->
          runWs driver.query
    

    查看runHalogenAff 的签名,我们可以看到它接受Aff (HalogenEffects eff) x 类型的参数,这意味着以下术语必须评估为该类型的值。那时runWs 一定会返回该类型的值。

    现在让我们转向runWs。它的参数是a natural transformation f ~&gt; m,在你的例子中是takes your query algebra into the Aff monad。我们可以把它写下来,然后让编译器为我们解决剩下的问题:

    runWs :: (Query ~> Aff _) -> Aff _ Unit
    

    这将成功构建并为您提供可以填补这些漏洞的东西。这是最终的签名:

    runWs :: forall eff. 
          (Query ~> Aff (HalogenEffects 
            ( console :: CONSOLE
            , err :: EXCEPTION
            , ws :: WEBSOCKET
            | eff
            )))
          -> Aff (HalogenEffects
            ( console :: CONSOLE
            , err :: EXCEPTION
            , ws :: WEBSOCKET
            | eff
            )) Unit
    

    这正是编译器输出给你的。我假设错误消息“类型变量 a 已超出其范围”是因为 the definition of a natural transformation 中的通用量词。

    【讨论】:

    • 我想知道你的脱糖建议要去哪里,但在我读了几次之后,我发现它真的很有帮助。
    • 不错的一个!在转义变量上,我确实确认这是在具有 N 级多态性的语言(如 PureScript)中类型推断的限制。在自然变换forall a. f a -&gt; g a 的定义中,在函数之外无法知道 a,但是类型推断本身并不足以解决这个问题,你最终会得到一个 skolem 常量(一个新的类型变量)转义它的范围。这就是为什么您必须自己提供签名的原因。在我观看了 Phil Freeman 的精彩视频后,这一切变得一清二楚:youtube.com/watch?v=SPpIbiZFPRY
    猜你喜欢
    • 2017-11-16
    • 2016-03-17
    • 1970-01-01
    • 2019-10-02
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    • 1970-01-01
    • 2017-06-02
    相关资源
    最近更新 更多