Skip to content

Instantly share code, notes, and snippets.

@DrBoolean
Last active December 1, 2020 03:12
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DrBoolean/352b354a47b97a8cf342 to your computer and use it in GitHub Desktop.
Save DrBoolean/352b354a47b97a8cf342 to your computer and use it in GitHub Desktop.
StateT is useful
import {StateT} from 'fantasy-states'
import Task from 'data.task'
import {prop, compose, map, chain, merge, always} from 'ramda'
// Unfortunately Binary Gendered Baby Page
//==========================================
// data App a b c = App State a (Task b c)
const App = StateT(Task)
const {get, put, modify} = App;
const babies = [{id: 2, name: 'Anjali', sex: 'F'}, {id: 3, name: 'Antonio', sex: 'M'}]
// isFemale :: Baby -> Bool
const isFemale = b => b.sex === 'F'
// findBaby :: Int -> Task String Baby
const findBaby = i => new Task((rej, res) => babies[i] ? res(babies[i]) : rej('None Found'))
// updatePrefs :: forall r. Baby -> App Prefs r Baby
const updatePrefs = b => modify(merge({bgColor: isFemale(b) ? 'pink' : 'blue'})).map(always(b))
// html :: Baby -> Prefs -> Html
const html = (b, prefs) => `<div style={background-color: ${prefs.bgColor}, font: ${prefs.font} }>${b.name}</div>`
// drawPage :: forall r. Baby -> App Prefs r Html
const drawPage = b => get.map(prefs => html(b, prefs))
// app :: App Int String Html
const app = compose(chain(drawPage), chain(updatePrefs), chain(compose(App.lift, findBaby)))
app(App.of(0)).evalState({font: 'cursive'}).fork(console.log, console.log)
//LOG: <div style={background-color: pink, font: cursive }>Anjali</div>
@DrBoolean
Copy link
Author

Here we use the transformer to compose State and Task. We can call lift if we end up with just a Task to cram it into our App type. Otherwise, our get/modify/put methods are already tailored to our type as they came off the transformer, not the vanilla State type.

@santios
Copy link

santios commented Feb 22, 2016

@DrBoolean Thank you for this example. As I understand, code before line 28 is pure. In my mind, pure code should be easy to test, but I can't think on a way to test the function findBaby for example. (Lets suppose the function actually makes an api call). Is there a way to test it without performing the fork in the test, as the fork will trigger the api call?. Ty Brian.

@DrBoolean
Copy link
Author

@santios

The way I like to test things is by placing a value into a Task: Task.of(['fake_results']). This gives the same semantics and composability of the actual api call, but without all the complexity of callbacks. It's still mocking, to be fair, but much easier to swap out and "reason about" as they say.

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