EscapedSkolem 在 PureScript 和 Halogen 中实现 websocket 重新连接时出错
EscapedSkolem error implementing websocket reconnection in PureScript and Halogen
我正在尝试在 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
编译器错误消息有时可能难以解密,但在这种情况下,它就是您正在寻找的答案。让我们在这里查看您的 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
returns 那个类型的值。
现在让我们转到runWs
。它的参数是a natural transformation f ~> m
which in your example 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
确实这正是编译器输出给你的。我假设错误消息 "the type variable a has escaped its scope" 是因为 the definition of a natural transformation.
中的通用量词
我正在尝试在 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
编译器错误消息有时可能难以解密,但在这种情况下,它就是您正在寻找的答案。让我们在这里查看您的 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
returns 那个类型的值。
现在让我们转到runWs
。它的参数是a natural transformation f ~> m
which in your example 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
确实这正是编译器输出给你的。我假设错误消息 "the type variable a has escaped its scope" 是因为 the definition of a natural transformation.
中的通用量词