1

This is regarding an attempt to get a WebSocket's input and output hooked up to a Coroutine.

The following function takes a Connection then sets it up to emit a Coroutine value when a message is received.

module Main where

import Prelude
import Control.Coroutine (emit, Producer, Consumer, await)
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)
import Control.Monad.Eff.Var (($=))
import Control.Monad.Reader.Trans (lift)
import Control.Monad.Rec.Class (forever)
import WebSocket (WEBSOCKET, Connection(..), newWebSocket, URL(..), runMessage, runMessageEvent)

wsProducer :: Connection → Producer String (Eff _) Unit
wsProducer (Connection s) = s.onmessage $= emit <<< runMessage <<< runMessageEvent

The Producer and Consumer will be hooked up in Main (the WebSocket connection will also be made there), that hasn't been written yet though, since the function above won't even typecheck.

How can this be made to typecheck, please? The fact it won't typecheck may mean there is a fundamental misunderstanding in the code above, if so an explanation with a code sample of a working solution would be very helpful.

Related: this answer about Halogen and WebSockets contains very similar code.

Community
  • 1
  • 1
user4301448
  • 191
  • 10

1 Answers1

4

There's a couple of things wrong with that snippet. First, here's a version that works:

module Main where

import Prelude

import Control.Coroutine (Producer)
import Control.Coroutine.Aff (produce)
import Control.Monad.Aff (Aff)
import Control.Monad.Aff.AVar (AVAR)
import Control.Monad.Eff.Var (($=))

import Data.Either (Either(..))

import WebSocket (WEBSOCKET, Connection(..), runMessageEvent, runMessage)

wsProducer :: forall eff. Connection → Producer String (Aff (avar :: AVAR, ws :: WEBSOCKET | eff)) Unit
wsProducer (Connection s) =
  produce \emit ->
    s.onmessage $= emit <<< Left <<< runMessage <<< runMessageEvent
  1. You're missing a use of produce, which is what brings emit into scope, and is how you make a producer.
  2. The producer must use Aff, not Eff.
  3. emit accepts an Either - a Left value indicates a value should be produced, and a Right indicates the producer should be closed.

Take a look at the docs for produce and hopefully the misunderstanding you mentioned will become clear!

gb.
  • 4,629
  • 1
  • 20
  • 19
  • I wondered if I should use `produce`. I was following the [purescript-coroutines documentation](https://pursuit.purescript.org/packages/purescript-coroutines/2.0.1), see the `nats` function, which uses `emit` but doesn't use `produce`. It's not at all clear in the docs what should be used under which circumstances, should `produce` be used when there are side-effects? – user4301448 Sep 26 '16 at 03:42
  • This code works (and I mostly get the explanation), thank you very much. Am afraid I don't have enough rep. to upvote your answer but I have marked it as the accepted answer. – user4301448 Sep 26 '16 at 04:19
  • 1
    Ah right, yes - `produce` is part of `-aff-coroutines`, which is for when you're working with an effectful producer of some kind. `-coroutines` only handles the general cases and has no opinion about the underlying monad. If you note in the docs you were referring to, the `m` is left entirely unspecified (aside from the `Monad` constraint) in the `Producer` since it is pure - it's only the consumer where `Eff` is introduced, due to the use of `log` in there. In this case, we have to do something effectful to be able to make the `Producer` even, hence the need for something like `produce`. – gb. Sep 26 '16 at 09:52
  • 1
    [`produce` itself doesn't consist of much code](https://github.com/purescript-contrib/purescript-aff-coroutines/blob/v3.0.0/src/Control/Coroutine/Aff.purs#L37-L61), so in theory you could just re-implement it yourself when you need it (it's a thin layer on top of some `AVar` usage), but it's a little tricky, and a fairly common usecase, which is why it exists as its own library too. – gb. Sep 26 '16 at 09:54
  • 1
    Great @gb., thanks for the clarification. I hope all this helps someone else too. – user4301448 Sep 26 '16 at 19:37