0
votes

While I was playing with PureScript, I found that I wanted to write a typeclass Sync that would wait for arbitrary asynchronous values to resolve in the Aff monad. The typeclass I wrote looked like this:

class Sync s eff a where
  sync :: s -> Aff eff a

Now I wanted to create Sync instance for a websocket connection, which would wait until the connection was open and available for reading/writing. The instance I wrote looked like this:

instance syncConnection :: Sync Connection (ws :: WEBSOCKET | eff) Unit where
  sync (Connection socket) =
    makeAff $ \fail continue ->
      set socket.onopen $ \_ ->
        continue unit

However, I got the following type error:

Type class instance head is invalid due to use of type

  ( ws :: WEBSOCKET
  | eff
  )

All types appearing in instance declarations must be of the form T a_1 .. a_n, where each type a_i is of the same form.

Coming from Haskell, this makes sense to me—it mirrors situations where I would need to enable the FlexibleInstances extension, which PureScript does not seem to support, either—but I’m left wondering if I can achieve the genericism I want at all.

I thought that perhaps I could tweak my Sync class, then just create a newtype.

class Sync s m a where
  sync :: s -> m a

newtype WebSocketAff a = WebSocketAff (Aff (ws :: WEBSOCKET) a)

Unfortunately, now I’m stuck yet again because I don’t know of any way to give WebSocketAff an instance of MonadAff for similar reasons to the ones I encountered at the beginning.

Are there any tricks I could use to get this to work without defeating the purpose of the genericism entirely? Or is that sort of thing currently not really expressible within PureScript?

2

2 Answers

1
votes

Since compiler version 0.10.3 you are allowed to use rows in the instance head as long as the row is determined via a FunDep:

e.g.

class Sync s eff a | s -> eff where -- FunDep here: eff is determinded via s
  sync :: s -> Eff eff a

data ConsoleSync = ConsoleSync

instance syncWithConsole :: Sync ConsoleSync (console :: CONSOLE | eff) Unit where
  sync _ = log "hello"

foo :: ∀ eff. Eff (console :: CONSOLE | eff) Unit
foo = sync ConsoleSync

Release Notes v0.10.3: https://github.com/purescript/purescript/releases/tag/v0.10.3

-1
votes

I'm on my mobile ATM so I can't test this, but did you try using forall?

instance syncConnection :: forall eff. Sync Connectionync Connection . . .