JavaScript Interop with Elm-Like (Spork)

#1

Hello. I hope everyone is having a good Friday. I’ve been trying to figure out how to get external JavaScript to send “events” to an Elm-Like written in PureScript. Given this excerpt from the Spork counter example:

type Model = Int

data Action = Inc | Dec

update ∷ Model → Action → Model
update i = case _ of
  Inc → i + 1
  Dec → i - 1

How could I send an Inc or Dec action from outside of the PureScript universe? To give a little context for my use case. I’m writing an app that uses JQuery select2, a multi-select picker, and I want to notify my PureScript app when an item is added or removed from the list of items (via an event). I’ve read the Spork code a bit in an effort to figure this out on my own to no avail.

Any help would be very much appreciated. Thanks!

#2

The interface for externally interacting with a running app is in AppInstance, which is returned by the make functions.
https://pursuit.purescript.org/packages/purescript-spork/0.6.0/docs/Spork.App#t:AppInstance

In particular, you would invoke app.push Inc followed by app.run. For FFI purposes I would probably export EffectFns that build and invoke what you want.

#3

Thanks, I’ll give this a shot.

#4

I started looking into this, and since the app instance is exposed inside of a do block in the main function, I’m not sure how to expose it to the JS side of the equation.

I’m also not sure how to trigger something resulting in an Effect from JavaScript in such a way that it gets run inside of main–after main is initially called.

Would you mind providing a super minimal example illustrating the FFI piece you are describing? Thanks!

#5

If you want to use the interface from JS, you invoke main or some other function from JS, and have it return the interface.

type FFIInterface =
  { inc :: Effect Unit
  , dec :: Effect Unit
  , run :: Effect Unit
  }

makeMySporkApp :: EffectFn1 String FFIInterface
makeMySporkApp = mkEffectFn1 \selector -> do
  app <- makeWithSelector ... selector
  let
    ffi =
      { inc: app.push Inc
      , dec: app.push Dec
      , run: app.run
      }
  pure ffi

Then invoke it from JS somehow.

const app = PS.MyApp.makeMySporkApp("#app");
app.inc();
app.dec();
app.run();
#6

Wow, I can’t thank you enough for the help. I got this working, and it is opening the door for me to build an app that leverages the broader JS ecosystem when needed while fully taking advantage of all of the safety and awesomness that PureScript has to offer. Truly the best of both worlds! Thank you!

#7

For anyone that stumbles onto this later and is looking for a fully working example, I put the following repo together.