Skip to content

Instantly share code, notes, and snippets.

@ryanartecona
Last active May 26, 2016 05:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanartecona/97ebff8d7bf0b30643a6475970d031eb to your computer and use it in GitHub Desktop.
Save ryanartecona/97ebff8d7bf0b30643a6475970d031eb to your computer and use it in GitHub Desktop.

Integrating PureScript into a Legacy JS Codebase

Tooling (w/ Electron)

  • compiled & used in an electron app
  • purescript is pulled in as an npm dep, and electron-compile has a step to compile PS to JS & move files to where they need to be.

Calling interop

For PS -> JS

Purescript has a Utils module which exports some types and functions, and that becomes a CommonJS module that can be require’d from JS.

Purescript native types map directly to native JS types, and share the same semantics (with minimal limitations), so anything valid in PS is automatically usable in JS.

For JS -> PS

A JS file defines some functions or whatever and exports them CommonJS style, and PureScript can just do a foreign import jsThing :: Int -> Int. This ties together a pair of JS and PS ‘modules’ which have the same name.

If JS can throw an exception, the ffi-imported function needs to catch it and turn it into an Either value, and return it as a value, since PS is incompatible with raw JS exceptions.

Friction

Cost of curried functions

PS functions are curried, so a function taking 3 arguments is compiled to a function which take the first, and returns a function which takes the second, etc., so larger code and more function calls (i.e. deeper stacks). Runtime cost is minimal in practice.

Immutable data

Immutability is enforced by not allowing mutability in the language, not by enforcing it by wrapping objects and arrays and only exposing immutable interfaces. This means it has zero runtime cost, since the code is guaranteed immutable at compile time, and then the resulting JS can just use raw objects/arrays/etc. Also no extra library or bundle bloat (Immutable.js is large)!

Team adoption path

Other compile-to-js langs didn’t quite satisfy requirements. PS did, but team was hesitant. Team lead was a big advocate.

Speaker sat down 1-on-1 to enumerate benefits of static typing.

TypeScript vs. PureScript came to a vote. PS won. Consensus was “if we’re moving off JS, let’s go all the way, and get max power”.

React app. Friction implementing first pass of integrating PS, mostly around frequently switching between PS and JS.

Move to use Pux as a React wrapper for PS. In the Redux store, updates notify Views (as normal), but also notify a Pux layer. Then the Pux layer could send out updates just like Views could. This enabled a cleaner, more incremental translation path.

Second big problem they ran into was around wrangling asynchronous computations that required holding a mutex. The JS attempt to this used an abstraction that worked like lock-aware Promises. The problem was that a nested async .then() callback had no way to know if an outer callback had already acquired the lock, and if it had, it would deadlock. There was no clear way to address this in JS. In PS, they used Aff (asynchronous effect types tracked in the type system) and StateT (a state-tracking type) to rig up a system which guaranteed acquired locks would not be reacquired, and thus prevented deadlocks.

Language transition among devs is rough. It’s not just a surface level difference from JS, and there are a lot of new concepts at once, and purity prohibits a lot of patterns which are possible and common in vanilla JS. Only 1 dev on team is able to do solo PS work, but they want to increase that number with pairing, talks, etc.

Lots of pairing helps. Organizing or joining a local community helps. The ability to peek into the compiled JS output (which is usually clean enough to read and understand) helps a lot. PS is much less scary when you see waht it compiles to.

Lessons learned

Things they thought were clean enough to be translated easily were not actually that clean.

Eff/Aff (the effect tracking systems) are intimidating at first, but not bad once you match it up with the simple effects they have on the compiled JS.

Should pick your battles on which things to convert.

Training is a high priority. As projects move from everyone working paired with the PS person and more individual solo, concerns come up on things not well understood.

Tooling is young compared with IDEs, but comparable to JS tooling.

@ryanartecona
Copy link
Author

Talk by David Koontz. Prezi presentation here: https://prezi.com/wgq786ck7uc8/from-javascript-to-purescript/. The Electron app this talk was based on is GitKraken, a Git GUI from Axosoft: https://www.gitkraken.com/features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment