PureScript <-> JavaScript Interop


#1

tmountain [2:34 PM]
If I want to create some standalone purescript functions that integrate with a broader javascript codebase, is there a way to take the output from browserify and accomplish that?

module GCD where

import Prelude

gcd2 :: Int -> Int -> Int
gcd2 n m | n == 0 = m
gcd2 n m | m == 0 = n
gcd2 n m | n > m = gcd (n - m) m
gcd2 n m = gcd (m - n) n

$ cat build.sh
pulp browserify --main GCD --skip-entry-point --standalone index --to js/index.js

As-in, is there some magic required to import index.js and expose gcd2?

natefaubion [2:36 PM]
At Awake, we would write something like a GCD.Interop, which has bindings from JS reps to the PS reps, and then we would just import the output directly

tmountain [2:37 PM]
Can you give an example of what that would look like specifically?

natefaubion [2:39 PM]

module GCD.Interop where
import Prelude
import GCD as GCD
import Data.Function.Uncurried (mkFn2)

gcd2 :: Fn2 Int Int Int
gcd2 = mkFn2 GCD.gcd2

(edited)
That gets compiled to ./output/GCD.Interop/index.js (edited)
and then in a js file I can do

gcd2(4, 5);

tmountain [2:40 PM]
Okay cool. That is helpful. Thank you!

natefaubion [2:40 PM]
you can import the original file directly as well
but the reason we write an Interop file is so we can change PS files without breaking JS consumers, or at least, if we change types, we know that JS consumers need to be verified (edited)

tmountain [2:41 PM]
If I load the original file via a script tag, I can’t figure out how to call gcd2, as it feels like it’s hidden behind a closure, am I wrong?
I don’t totally understand the magic browserify is doing…

natefaubion [2:42 PM]
If you use browserify with standalone, then you can configure what the global variable is
so pulp browserify --main GCD --skip-entry-point --standalone GCD --to index.js (edited)
if you load that index file with a script tag
then you’ll have GCD.gcd2 available in the global namesapce (edited)


#2

For anyone looking for a fully working example, here goes.

$ cat src/GCD.purs
module GCD where

import Prelude

gcd2 :: Int -> Int -> Int
gcd2 n m | n == 0 = m
gcd2 n m | m == 0 = n
gcd2 n m | n > m = gcd (n - m) m
gcd2 n m = gcd (m - n) n
$ cat src/GCD/Interop.purs
module GCD.Interop where
import GCD as GCD
import Data.Function.Uncurried (Fn2, mkFn2)

gcd2 :: Fn2 Int Int Int
gcd2 = mkFn2 GCD.gcd2

If you just want the original GCD file without the wrapper, do this.

pulp browserify --main GCD --skip-entry-point --standalone GCD --to js/index.js

Embed in your HTML as usual:

<script src="js/index.js"></script>

Then, you can invoke the curried version from the browser console.

> GCD.gcd2(10)(5)
> 5

To build the wrapped version, do this.

pulp browserify --main GCD.Interop --skip-entry-point --standalone GCD --to js/index.js

Because of the wrapper, you can do a typical JavaScript invocation.

> GCD.gcd2(10, 5)
> 5