You’ve started a new project at work with a tight deadline. You’re in charge of picking the stack and after assessing technologies best for the project you’ve decided to use PureScript on the front-end. One factor determining the success of the project is that it be delivered on time.
As is typical for jQuery plugins, jQuery-steps takes a configuration object to tell it which DOM elements it should bind to. We begin by defining the types we’ll need to safely represent the fields of this configuration object in PureScript.
We use newtype wrappers to represent the values of the fields in our configuration object. We do this because newtypes have the same runtime representation as the underlying type, whilst being distinct from the perspective of the type checker. This makes newtypes an easy win for providing an extra layer of type safety when defining FFI bindings.
We define a default configuration object to let consumers of our bindings use record update syntax to override the defaults we’ve set.
Here’s what defaultConfig will look like at runtime. You can see that despite adding an extra layer of type safety when working with our configuration object in PureScript, we don’t pay any extra cost for the abstraction at runtime. Practically speaking, this means that we can pass our configuration object directly to jQuery-steps.
And, finally, we write a foreign import declaration and we’re done. It’s really as simple as that. Well, almost.
Callbacks and Dishonesty
We begin by extending Config to include two new fields: onFinished and onFinishing. The type constructor Callback2 comes from purescript-foreign-callbacks and represents an effectful computation that takes two arguments, whilst the type constructor Fn3 comes from purescript-functions and represents a pure function that takes three arguments.
mkFinished, on the other hand, is much simpler. It uses callback2 to take our effectful computation of two arguments and transforms it into an appropriate runtime representation for jQuery-steps.
And, finally, here’s what the resulting defaultConfig looks like. Now we’re really done. That wasn’t so bad, was it?