Question: Halogen and Websockets

halogen
#1

Hi! I’ve been playing around with Purescript and Halogen and I stumbled upon a problem I can’t seem to figure out how to solve.

In all the Websocket examples I’ve seen the connection is created in main, however in my case I need to be able to setup the connection after a user logs in as this determines the url for the connection.

I’m currently using the purescript-web-socket package, the websocket-simple looks somewhat… well… simpler though.

I had look at: https://github.com/nathanic/purescript-simple-chat-client which at first seemed like it would be helpful however that example seems to be a bit dated.

Thanks!

#2

I’m assuming some things about your component architechture. Namely I’m assuming you’ve hoist ed your component tree into the Aff monad or some monad that has the capabilities of Aff. If not, you will need to do that. check out Real World Halogen to see examples on how to do this (I believe it uses ReaderT) https://github.com/thomashoneyman/purescript-halogen-realworld

So your top-level application component could have some state, a Maybe WebSocket for example, and the websocket can be created only following a successful log in. if your architecture is such that you have a parent component which the user sees only after they’ve logged in, then it is probably safest that the Maybe Websocket state live in that parent component, and that an action is launched from the init function which creates the socket and sets it into state.

if you wish to give child components access to the socket directly, you can pass the socket down as input or messages, Alternatively your parent component could serve as a hub for all messages which need to be passed to/from the WebSocket

#3

@erikbackman Setting up a websocket connection happens in Effect, which means that you can do it anywhere you can run effectful code. That’s certainly the case in your main function, but it’s also the case in your Halogen components (so long as the component can be run in Aff or Effect).

We can run with @Benjmhart’s suggestion and say that your component receives the connection URL as input. This would be a fairly common type to represent that:

type Input = { url :: String }

component :: H.Component HH.HTML query Input output Aff

-- or
component :: MonadAff m => H.Component HH.HTML query Input output m

This component can, when it initializes, start the websocket connection. For example, you might do something like this:

data Action = Initialize

component = ...
  { initializer = Just Initialize
  , handleAction = handleAction
  }
  where
  handleAction = case _ of
    Initialize -> do
      { url } <- H.get
      H.liftEffect do
        -- whatever code you want to run in Effect, like open 
        -- a websocket at the provided `url`, just like you 
        -- would write in your `main` function

I haven’t done any work with websockets myself so I’m not sure exactly what you need to do here. Even so, this is how you might pass the URL the user needs to connect to down to a component which actually starts up that connection. You may need to add a finalizer which closes the websocket when the component unmounts.


As far as further examples go, these may be useful to you (they use purescript-web-socket):

I often look for example projects using libraries that I’m also using by searching the library name in GitHub and filtering by language:purescript. For example, to find projects using websocket-simple, you can search purescript-websocket-simple language:purescript.


As a side note, the purescript-web organization supplies types and low-level implementations for various specs, but the libraries aren’t much fun to use. If websocket-simple can do what you need there’s no reason to prefer the purescript-web libraries.

#4

I feel so silly now, not sure why I struggled to realise this. Thanks @thomashoneyman and @Benjmhart!

1 Like