Skip to content

Instantly share code, notes, and snippets.

@rjdestigter
Created October 23, 2018 18:11
Show Gist options
  • Save rjdestigter/5306eef85534f73f826d6988f84812d4 to your computer and use it in GitHub Desktop.
Save rjdestigter/5306eef85534f73f826d6988f84812d4 to your computer and use it in GitHub Desktop.
map and flatMap implementation for selectors
interface Fruit {
isRotten: boolean
}
interface Apple extends Fruit {
apple: true
}
interface Banana extends Fruit {
banana: true
}
interface State {
apples: Apple[]
bananas: Banana[]
selected: Set<'Apples' | 'Bananas'>
}
const isRotten = <F extends Fruit>(fruit: F) => fruit.isRotten
const edibleFruits = <F extends Fruit>(fruits: F[]) => fruits.filter(fruit => !isRotten(fruit))
const apples$ = (state: State) => state.apples
const bananas$ = (state: State) => state.bananas
const selectedFruits$ = (state: State) => state.selected
const edibleApples$ = map(apples$)(edibleFruits)
const edibleBananas$ = map(bananas$)(edibleFruits)
const selectedFruitsArray$ = map(selectedFruits$)(selected => Array.from(selected))
const fruitBasket$ = flatMap(selectedFruitsArray$)(fruits => {
const selectors$: Selector<State, Fruit[]>[] = fruits.map(fruit => {
switch (fruit) {
case 'Apples':
return apples$
case 'Bananas':
return bananas$
}
})
return createSelector(selectors$, (...allFruits) => ([] as Fruit[]).concat(...allFruits))
})
import { createSelector, defaultMemoize, Selector } from 'reselect'
/**
* Map (fmap) from selector A to selector B.
*
* @param f Function that maps a -> b
* @param selector$ Selector that, given state S returns A
* @returns A selector that, given state S, returns B
*/
export const map = <S, A>(selector$: Selector<S, A>) => <B>(f: (a: A) => B) =>
createSelector(selector$, outputA => f(outputA))
/**
* Flat map a selector of a selector. Also known as "bind" (haskell) or "chain" (fp-ts)
* @param f Function that, given the result of selector A returns a selector of B
* @param a$ A selector of A
* @returns A function that, given state S, returns B
*/
export const flatMap = <S, A>(a$: Selector<S, A>) => <B>(f: (a: A) => Selector<S, B>) => {
let previousA: A | undefined
let b$: Selector<S, B> | undefined
return defaultMemoize((state: S) => {
const nextA = a$(state)
if (nextA !== previousA || b$ == null) {
previousA = nextA
b$ = f(previousA)
}
return b$(state)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment