Last active
September 12, 2018 04:20
-
-
Save zerobias/84925f30334eee0d419302d30d60bbd7 to your computer and use it in GitHub Desktop.
Transmutation with effector (aka histomorphism)
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
//@flow | |
import {createStore, type Store} from 'effector' | |
const capacity: '@@capacity' = (Symbol('buffer capacity'): any) | |
declare export function readCapacity<T>(store: Store<[T, T, T, T, T, T, T]>): 7 | |
declare export function readCapacity<T>(store: Store<[T, T, T, T, T, T]>): 6 | |
declare export function readCapacity<T>(store: Store<[T, T, T, T, T]>): 5 | |
declare export function readCapacity<T>(store: Store<[T, T, T, T]>): 4 | |
declare export function readCapacity<T>(store: Store<[T, T, T]>): 3 | |
declare export function readCapacity<T>(store: Store<[T, T]>): 2 | |
declare export function readCapacity<T>(store: Store<[T]>): 1 | |
declare export function readCapacity<T>(store: Store<Array<T>>): number | |
declare export function readCapacity<T>(store: Store<T>): 0 | |
export function readCapacity(store: mixed): number { | |
return typeof store === 'object' && | |
store !== null && | |
typeof store[capacity] === 'number' | |
? store[capacity] | |
: 0 | |
} | |
declare export function createBuffer<T>(n: 1, store: Store<T>): Store<[T]> | |
declare export function createBuffer<T>(n: 2, store: Store<T>): Store<[T, T]> | |
declare export function createBuffer<T>(n: 3, store: Store<T>): Store<[T, T, T]> | |
declare export function createBuffer<T>( | |
n: 4, | |
store: Store<T>, | |
): Store<[T, T, T, T]> | |
declare export function createBuffer<T>( | |
n: 5, | |
store: Store<T>, | |
): Store<[T, T, T, T, T]> | |
declare export function createBuffer<T>( | |
n: 6, | |
store: Store<T>, | |
): Store<[T, T, T, T, T, T]> | |
declare export function createBuffer<T>( | |
n: 7, | |
store: Store<T>, | |
): Store<[T, T, T, T, T, T, T]> | |
declare export function createBuffer<T>( | |
n: number, | |
store: Store<T>, | |
): Store<$ReadOnlyArray<T>> | |
export function createBuffer<T>(n: number, store: Store<T>): Store<Array<T>> { | |
const buffered = store.map((data, state: Array<T>) => { | |
if (state.length < n - 1) return [data, ...state] | |
return [data, ...state.slice(0, -1)] | |
}, []) | |
//$off | |
buffered[capacity] = n | |
return buffered | |
} | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T]>, | |
pred: (a: T) => boolean, | |
map: (a: T) => T, | |
}>, | |
): Store<[T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T, T]>, | |
pred: (a: T, b: T) => boolean, | |
map: (a: T, b: T) => T, | |
}>, | |
): Store<[T, T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T, T, T]>, | |
pred: (a: T, b: T, c: T) => boolean, | |
map: (a: T, b: T, c: T) => T, | |
}>, | |
): Store<[T, T, T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T, T, T, T]>, | |
pred: (a: T, b: T, c: T, d: T) => boolean, | |
map: (a: T, b: T, c: T, d: T) => T, | |
}>, | |
): Store<[T, T, T, T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T, T, T, T, T]>, | |
pred: (a: T, b: T, c: T, d: T, e: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T) => T, | |
}>, | |
): Store<[T, T, T, T, T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T, T, T, T, T, T]>, | |
pred: (a: T, b: T, c: T, d: T, e: T, f: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T, f: T) => T, | |
}>, | |
): Store<[T, T, T, T, T, T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<[T, T, T, T, T, T, T]>, | |
pred: (a: T, b: T, c: T, d: T, e: T, f: T, _: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T, f: T, _: T) => T, | |
}>, | |
): Store<[T, T, T, T, T, T, T]> | |
declare export function createTransit<T>( | |
opts: $ReadOnly<{ | |
store: Store<$ReadOnlyArray<T>>, | |
pred: (...values: Array<T>) => boolean, | |
map: (...values: Array<T>) => T, | |
}>, | |
): Store<$ReadOnlyArray<T>> | |
export function createTransit<T>({ | |
store, | |
pred, | |
map, | |
}: { | |
store: Store<Array<T>>, | |
pred: (...values: Array<T>) => boolean, | |
map: (...values: Array<T>) => T, | |
}): Store<Array<T>> { | |
const size = readCapacity(store) | |
const result = store.map(list => { | |
if (list.length < size) return list | |
if (pred(...list)) return [map(...list), ...list.slice(1)] | |
return list | |
}) | |
//$off | |
result[capacity] = size | |
return result | |
} | |
export function readHead<T>(store: Store<$ReadOnlyArray<T>>): Store<T> { | |
return store.map(([state]) => state) | |
} | |
declare export function transition<T>(opts: { | |
n: 7, | |
store: Store<T>, | |
pred: (a: T, b: T, c: T, d: T, e: T, f: T, _: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T, f: T, _: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 6, | |
store: Store<T>, | |
pred: (a: T, b: T, c: T, d: T, e: T, f: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T, f: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 5, | |
store: Store<T>, | |
pred: (a: T, b: T, c: T, d: T, e: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 4, | |
store: Store<T>, | |
pred: (a: T, b: T, c: T, d: T) => boolean, | |
map: (a: T, b: T, c: T, d: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 3, | |
store: Store<T>, | |
pred: (a: T, b: T, c: T) => boolean, | |
map: (a: T, b: T, c: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 2, | |
store: Store<T>, | |
pred: (a: T, b: T) => boolean, | |
map: (a: T, b: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 1, | |
store: Store<T>, | |
pred: (a: T) => boolean, | |
map: (a: T) => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: 0, | |
store: Store<T>, | |
pred: () => boolean, | |
map: () => T, | |
}): Store<T> | |
declare export function transition<T>(opts: { | |
n: number, | |
store: Store<T>, | |
pred: (...args: Array<T>) => boolean, | |
map: (...args: Array<T>) => T, | |
}): Store<T> | |
export function transition<T>({ | |
n, | |
store, | |
pred, | |
map, | |
}: { | |
n: number, | |
store: Store<T>, | |
pred: (...args: Array<T>) => boolean, | |
map: (...args: Array<T>) => T, | |
}): Store<T> { | |
const buffer: Store<$ReadOnlyArray<T>> = createBuffer(n, store) | |
return readHead(createTransit({store: buffer, pred, map})) | |
} | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 7, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T, b: T, c: T, d: T, e: T, f: T, _: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T, f: T, _: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 6, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T, b: T, c: T, d: T, e: T, f: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T, f: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 5, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T, b: T, c: T, d: T, e: T) => boolean, | |
map: (a: T, b: T, c: T, d: T, e: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 4, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T, b: T, c: T, d: T) => boolean, | |
map: (a: T, b: T, c: T, d: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 3, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T, b: T, c: T) => boolean, | |
map: (a: T, b: T, c: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 2, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T, b: T) => boolean, | |
map: (a: T, b: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: 1, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (a: T) => boolean, | |
map: (a: T) => T, | |
}>, | |
): Store<T> | |
declare export function transmutation<T, M>( | |
opts: $ReadOnly<{ | |
n: number, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (...args: Array<T>) => boolean, | |
map: (...args: Array<T>) => T, | |
}>, | |
): Store<T> | |
export function transmutation<T, M>({ | |
n, | |
store, | |
pred, | |
of, | |
map, | |
}: $ReadOnly<{ | |
n: number, | |
store: Store<M>, | |
of: (value: M) => T, | |
pred?: (...args: Array<T>) => boolean, | |
map: (...args: Array<T>) => T, | |
}>): Store<T> { | |
return transition({ | |
n: (n: typeof n), | |
store: store.map(of), | |
pred: pred || (() => true), | |
map, | |
}) | |
} | |
export class Transmutator<T> { | |
mappers: Array<{ | |
n: number, | |
pred(...args: Array<T>): boolean, | |
map(...args: Array<T>): T, | |
}> | |
constructor( | |
mappers: Array<{ | |
n: number, | |
pred(...args: Array<T>): boolean, | |
map(...args: Array<T>): T, | |
}> = [], | |
) { | |
this.mappers = mappers | |
} | |
variant( | |
map: (a: T) => T = a => a, | |
pred: (a: T) => boolean = a => true, | |
): Transmutator<T> { | |
return new Transmutator([...this.mappers, {n: 1, pred, map}]) | |
} | |
variant2( | |
map: (a: T, b: T) => T = (a, b) => a, | |
pred: (a: T, b: T) => boolean = (a, b) => true, | |
): Transmutator<T> { | |
return new Transmutator([...this.mappers, {n: 2, pred, map}]) | |
} | |
variant3( | |
map: (a: T, b: T, c: T) => T = (a, b, c) => a, | |
pred: (a: T, b: T, c: T) => boolean = (a, b, c) => true, | |
): Transmutator<T> { | |
return new Transmutator([...this.mappers, {n: 3, pred, map}]) | |
} | |
variant4( | |
map: (a: T, b: T, c: T, d: T) => T = (a, b, c, d) => a, | |
pred: (a: T, b: T, c: T, d: T) => boolean = (a, b, c, d) => true, | |
): Transmutator<T> { | |
return new Transmutator([...this.mappers, {n: 4, pred, map}]) | |
} | |
predVariant(pred: (a: T) => boolean = a => true): Transmutator<T> { | |
return this.variant(a => a, pred) | |
} | |
predVariant2( | |
pred: (a: T, b: T) => boolean = (a, b) => true, | |
): Transmutator<T> { | |
return this.variant2((a, b) => a, pred) | |
} | |
predVariant3( | |
pred: (a: T, b: T, c: T) => boolean = (a, b, c) => true, | |
): Transmutator<T> { | |
return this.variant3((a, b, c) => a, pred) | |
} | |
predVariant4( | |
pred: (a: T, b: T, c: T, d: T) => boolean = (a, b, c, d) => true, | |
): Transmutator<T> { | |
return this.variant4((a, b, c, d) => a, pred) | |
} | |
of<M>(store: Store<M>, of: (_: M) => T): Store<T> { | |
const first: Store<T> = store.map(of) | |
let next: Store<T> = first | |
for (const {n, pred, map} of this.mappers) { | |
next = transition({n: (n: typeof n), store: next, map, pred}) | |
} | |
return next | |
} | |
} | |
const foo: Store<number> = createStore(0) | |
const fooTransit = transmutation({ | |
n: (3: 3), | |
store: foo, | |
of: (n: number): [number, 'asc' | 'dec' | 'unknown'] => [n, 'unknown'], | |
map([an], [bn, bt], [cn, ct]): [number, 'asc' | 'dec' | 'unknown'] { | |
if (an > bn && bn > cn) return [an, 'asc'] | |
if (an < bn && bn < cn) return [an, 'dec'] | |
return [an, 'unknown'] | |
}, | |
}) | |
const mut: Transmutator< | |
[number, 'asc' | 'dec' | 'unknown'], | |
> = new Transmutator() | |
const mutResult = mut | |
.variant3( | |
([an]) => [an, 'asc'], | |
([an], [bn, bt], [cn, ct]) => an > bn && bn > cn, | |
) | |
.variant3( | |
([an]) => [an, 'dec'], | |
([an], [bn, bt], [cn, ct]) => an < bn && bn < cn, | |
) | |
.of(foo, n => [n, 'unknown']) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment