0

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?

Alexis King
  • 43,109
  • 15
  • 131
  • 205

2 Answers2

1

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

pete
  • 146
  • 1
  • Wow, thanks for responding to this old question, this is really awesome to find out about. Back when I wrote this question, PS didn’t even support functional dependencies, but this totally solves my use case. Exciting! – Alexis King Dec 14 '16 at 04:05
-1

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 . . .

clinux
  • 2,984
  • 2
  • 23
  • 26