Brandon Konkle
Brandon Konkle

Engineering Manager, type system nerd, Rust enthusiast, supporter of social justice, loving husband & father, avid comic & manga reader, 日本語を勉強してる。

I’m a Software Architect with more than 15 years of experience creating high performance server and front-end applications targeting web and mobile platforms, & today I lead a team at Formidable Labs.

Share


Tags


“Gradual” PureScript

Teaching PureScript and JavaScript to get along together

When I first started exploring PureScript, I was under the impression that it was an all-or-nothing commitment. The tutorials I found all assumed that you were starting a new PureScript project from scratch, building with the community tool pulp, and running your application with the Main.purs module. This is certainly a great way to manage your project, but it’s not always feasible for existing projects.

Webpack to the rescue!

Thankfully there’s a great PureScript loader for Webpack, which has emerged as the leading build tool in the React community. With purs-loader you can load PureScript files like any other module in your existing JavaScript code. Here’s how I have it configured:

module: {
  // PureScript
  loaders: [{
    test: /\.purs$/,
    loader: 'purs',
    exclude: /node_modules/,
    query: {
      psc: 'psa',
      src: ['bower_components/purescript-*/src/**/*.purs', 'src/**/*.purs'],
      warnings: false,
    },
  }]
}

I’m using purescript-psa as the compiler frontend, because it gives nicely formatted and colorized results. With this configuration in place, I can now import PureScript like any other JavaScript module!

Here’s an example. Let’s say I have a PureScript component based on recompose that handles the logic for adding a new “todo” object:

module Todo.Components.NewTodo (newTodo) where

import Prelude
import Control.Monad.Eff (Eff)
import React (ReactClass)
import React.Recompose (withHandlers)
import Todo.State.Todos (add) as Todos
import Redux.Mini (connect)

-- Props

type HandleAdd props eff =
  { add :: String -> Eff eff Unit | props } ->
  { key :: String, target :: { value :: String } } ->
  Eff eff Unit

addTodo :: forall props eff. HandleAdd props eff
addTodo props event = case event.key of
  "Enter" -> props.add event.target.value
  _ -> pure unit

-- Component

newTodo :: ReactClass {}
newTodo = connectState <<< eventHandlers
  where mapStateToProps = \state -> { nextId: show $ state.todos.lastId + 1 }
        dispatchActions = { add: Todos.add }
        connectState = connect mapStateToProps dispatchActions
        eventHandlers = withHandlers { addTodo }

I already have a nice presentational component written in JSX that I’d like to use this with, so I simply import my PureScript higher-order component and wrap my presentational component with it:

import React, {Component} from 'react'
import {newTodo} from './NewTodo.purs'

class NewTodoView extends Component {
  render () {
    const {addTodo, nextId} = this.props
    return (
      <header className="header">
        <h1>todos</h1>
        <input className="new-todo"
          key={nextId}
          placeholder="What needs to be done?"
          autoFocus
          name="newTodo"
          onKeyPress={addTodo} />
      </header>
    )
  }
}

export default newTodo(NewTodoView)

Now I have my type-safe PureScript logic easily attached to my dynamic presentational component. I can use this to re-write all of my sensitive operations in PureScript, and leave low-risk operations like style and layout to my dynamic JavaScript code.

Cleaning up input

One challenge to this approach is that the input coming in to your PureScript components is not strongly typed and can be unpredictable (and purescript-recompose itself doesn’t enforce any specific types). Using PureScript to manage your entire state system is one way to mitigate this risk — all of your logic depends on values retrieved from type-safe state management. This isn’t always practical, though, especially in legacy projects.

The purescript-foreign library provides some great tools for validating and cleaning up unpredictable foreign input — which is exactly what we’re talking about here. If you keep your state clean and use easily serializable values for everything, then validating your input could be as easy as this simple JSON example. If not, you’ll want to take a look at some of the more complex examples.

You decide what to type

With this strategy you can embrace gradual typing and choose which parts of your application need to be strongly typed, and which don’t. This can be a very powerful approach for front end applications, which often don’t need strict type safety in the presentational layer but could really benefit from it in business logic and API integration.

My strategy is influenced by an excellent talk from Gary Bernhardt — Boundaries. Gary builds up to a pattern he calls “functional core, imperative shell”, where a dynamic outer layer manages the practical integration of the deterministic functional core with the unpredictable real world. I like using PureScript to strictly manage my state and wrap my presentational components with composable logic.

This is also a way to introduce PureScript to a project incrementally over time. New features could use PureScript, while legacy code is transformed over time.

We’re here to help!

As we begin to expand the team and take on new projects we’ll be exploring strong typing, functional programming, and languages like PureScript to make the chaotic environment of front end engineering more stable and predictable. We’re building out universal applications with a shared core that works on the web, mobile devices, and desktop platforms — and this makes the code at the center more critical than ever. We’ll be sharing our progress frequently along our journey.

If you’re building an application on Node, React, and similar technologies we’d love to help! We offer hourly consulting at Ecliptic, and we can take the lead on your project or augment your existing team to help you build the best experience for your users.

Join us on our brand new Gitter chat community, tweet at us, or send us an email. We’re glad to answer questions, chat about big ideas, or just say hi!

I’m a Software Architect with more than 15 years of experience creating high performance server and front-end applications targeting web and mobile platforms, & today I lead a team at Formidable Labs.

View Comments