Last active
July 3, 2020 15:35
-
-
Save Willmo36/47bdb7b5f58b680f874b05afd2df645f to your computer and use it in GitHub Desktop.
fp-ts comonad builder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Comonad2C } from 'fp-ts/lib/Comonad'; | |
import { FunctionN } from 'fp-ts/lib/function'; | |
import { Monoid } from 'fp-ts/lib/Monoid'; | |
declare module 'fp-ts/lib/HKT' { | |
interface URItoKind2<E, A> { | |
Builder: Builder<E, A>; | |
} | |
} | |
export const URI = 'Builder'; | |
export type URI = typeof URI; | |
export interface Builder<A, B> { | |
_tag: URI; | |
(opts: A): B; | |
} | |
export const getComonadBuilder = <E>(M: Monoid<E>): Comonad2C<URI, E> => ({ | |
URI, | |
_E: M.empty, | |
map: (fa, f) => mkBuilder(pa => f(fa(pa))), | |
extract: builder => builder(M.empty), | |
extend: (builder, setter) => | |
mkBuilder(opts2 => | |
setter(mkBuilder(opts1 => builder(M.concat(opts1, opts2)))), | |
), | |
}); | |
export const mkBuilder = <E, B>(fn: FunctionN<[E], B>): Builder<E, B> => { | |
let b = fn as Builder<E, B>; | |
b._tag = URI; | |
return b; | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type MyState = { | |
store1: {foo: number}, | |
store2: {bar: number} | |
}; | |
const initialState: MyState = { | |
store1: {foo: 0}, | |
store2: {bar: 0}, | |
} | |
//A store of stores | |
//allow for partial updating of a partial set of stores | |
//aka | |
type DoublePartial<T> = Partial<{[K in keyof T]: Partial<T[K]>}>; | |
export type StateBuilder = Builder<DoublePartial<MyState>, MyState>; | |
export type StateBuilderStep = (builder: StateBuilder) => MyState; | |
const monoidMyState: Monoid<DoublePartial<MyState>> = { | |
empty: initialState, | |
concat: (a, b) => { | |
return { | |
store1: {...a.store1, ...b.store1}, | |
store2: {...a.store2, ...b.store2}, | |
}; | |
}, | |
}; | |
//We can use the Partial.concat here to save duplication | |
//but at the cost of discarding | |
const fromDoublePartial = (partial: DoublePartial<MyState>): MyState => | |
// cast here to reuse the code above (sligtly cheating but saves LoC) | |
monoidMyState.concat(initialState, partial) as MyState; | |
export const comonadMyState = getComonadBuilder(monoidMyState); | |
export const buildFromSteps = (steps: StateBuilderStep[]) => { | |
const combinedBuilder = steps | |
.reverse() | |
.reduce(comonadMyState.extend, baseBuilder); | |
return comonadMyState.extract(combinedBuilder); | |
}; | |
/** | |
* Given a final config (DoublePartial<MyState>) | |
* turn it into a MyState | |
*/ | |
const baseBuilder: StateBuilder = makeBuilder(fromDoublePartial); | |
/** | |
* BUILDERS | |
*/ | |
export const withRandomFoo: StateBuilderStep = builder => | |
builder({store1: {foo: Math.random()}}); | |
export const withRandomBar: StateBuilderStep = builder => | |
builder({store2: {bar: Math.random()}}); | |
export const state = buildFromSteps([ | |
withRandomBar, | |
withRandomFoo | |
]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment