PureScript: Connecting It With JavaScript
Parent post: Thoughts on building a PureScript app
PureScript is a language that’s compiled to JavaScript. When I first thought about this, I kinda understood it, but not quite. Okay, the output is valid JavaScript, but how do the two languages relate? If I write something in PureScript, how do I use it in JavaScript?
And vice versa: all those JavaScript libraries, how can I import them in PureScript?
Here’s how I think about it: JavaScript is this messy language full of unsafe and bad and ugly and PureScript should be it’s… well, pure cousin. But I can’t use PureScript in isolation. At the end, it will take data from the real messy world, it will communicate with JavaScript, so I better learn how to separate the two worlds in a safe manner.
Calling PureScript functions
Lets create a simple module which will contain an add function:
1 2 3 4 | |
Saving this little script to MyMath.purs and compiling it with:
psc MyMath.purs --browser-namespace PureScript > index.js
will generate a file which we can import in our test index.html page:
1 2 3 4 5 6 7 8 | |
Upon loading the script, we can access our function like this:
1 2 | |
A couple of notes here:
- The
--browser-namespace PureScriptflag I used told the compiler to export all modules in an object calledPureScript. If you leave this flag out, the name of this object will default toPS - The reason why I call
MyMath.add(3)(4)in this rather strange way is because functions exported by PureScript always take exactly zero or one arguments.MyMath.add(3)in fact returns a new function which takes the second argument, which feels really strange if you’ve never seen it before. If you want to know more about why it is like that, you can check the Wiki article on currying
Co calling PureScript code is easy:
- Write your modules
- Compile them into an
index.jsscript - Import the script in the
index.htmlpage - Call your functions
Calling JavaScript functions
The first resource you want to check out is the PureScript documentation on Foreign Function Interfaces.
The next thing you need to realize is this: at the end of the day, PureScript is just JavaScript that runs in some context. There are global variables around. You just can’t see them by default.
Let’s upgrade the example above. We’ll define a function in the global scope:
1 2 3 4 5 6 7 8 9 10 11 | |
Now let’s reference it from our compiled PureScript code:
1 2 3 4 5 6 7 8 9 | |
And finally, let’s call our new function:
1 2 | |
Let’s revise. In order to call a JavaScript function from PureScript, you need to:
- Have the function defined in the scope
- Import it with
foreign import yourFunction :: InputType -> OutputType - Use it like it’s a PureScript function
Hopefully, life makes sense now.
Important closing notes
There are other ways of compiling PureScript
Instead of running psc which combines all modules in a single file, you can use psc-make. The difference is: psc-make exports CommonJS modules in separate folders in a directory called output.
This is particularly interesting if you’re using PureScript functions from Node.js. If you copy the contents of output you should be able to do this:
1 2 | |
You can define JS functions inline
Instead of writing:
1
| |
which relies on having happySadParity in scope, you can just define the function on the fly:
1 2 3 4 | |
Is it safe?
No, it’s not. By binding code this way, you get none of the safety that PureScript offers.
Think about it. If you shove an exception in a PureScript function that doesn’t know how to handle it, well, your script will crash, no matter how typechecked the language is.
Likewise, if you call a JavaScript function and you’re not prepared for it to to return null or undefined — you’re screwed.
How do I make it safe?
I’ll cover that topic in another post which I’ll link here.