Skip to content

Commit

Permalink
Merge pull request #223 from Phuire-Research/Consistency
Browse files Browse the repository at this point in the history
Consistency
  • Loading branch information
REllEK-IO committed May 16, 2024
2 parents 3a7a0dd + 188117d commit 14c0fa5
Show file tree
Hide file tree
Showing 20 changed files with 523 additions and 19 deletions.
12 changes: 11 additions & 1 deletion ActionStrategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,16 @@ export const createAsyncMethodThrottle =
export const createAsyncMethodThrottleWithState =
(asyncMethodWithState: (controller: ActionController, action: Action, concepts: Concepts) =>
void, concepts$: UnifiedSubject, semaphore: number, duration: number): [Method, Subject<Action>] => {}

export const createMethodBuffer =
(method: (action: Action) => Action, duration: number): [Method, Subject<Action>] => {}
export const createMethodBufferWithState =
(methodWithState: (action: Action, concepts: Concepts) => Action, concepts$: UnifiedSubject, semaphore: number, duration: number)
: [Method, Subject<Action>] => {}
export const createAsyncMethodBuffer =
(asyncMethod: (controller: ActionController, action: Action) => void, duration: number): [Method, Subject<Action>] => {}
export const createAsyncMethodBufferWithState =
(asyncMethodWithState: (controller: ActionController, action: Action, concepts: Concepts) =>
void, concepts$: UnifiedSubject, semaphore: number, duration: number): [Method, Subject<Action>] => {}
```
* createMethod - Your standard method, be sure to handle the action.strategy via one of the strategy decision functions, in addition to passing the action if there is no attached strategy.
* createMethodWithState - This will allow your method to have the most recent state to be accessed via the asyncMethod function.
Expand All @@ -198,6 +207,7 @@ export const createAsyncMethodThrottleWithState =
* createMethodThrottleWithState- Fires the first action, alongside the most recent state, then filters rest as conclude.
* createAsyncMethodThrottle - Asynchronously fires the first action, will filtering the rest for the set duration as conclude.
* createAsyncMethodThrottleWithState - Fires the first action asynchronously with the most recent state, and filters action during the duration as conclude to remove stale tickers from ownership if loaded.
* **Buffer Series** similar to debounce method series, but will issue each possible action that encounters the quality for a length of time. Note these will fail ActionStrategies whose time has expired.

## "Creator Functions"
Note here this is merely a guideline to inform the creation of your strategies.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ When in doubt simplify.
* [Unified Turing Machine](https://github.com/Phuire-Research/Stratimux/blob/main/The-Unified-Turing-Machine.md) - The governing concept for this entire framework.

## Change Log ![Tests](https://github.com/Phuire-Research/Stratimux/actions/workflows/node.js.yml/badge.svg)
### Consistency Update v0.1.70 5/16/2024
* Added new buffer method series that will delay the dispatch of some possible set of actions for a period of time.
* Finally removed the need to add "as Subject<Concepts> | UnifiedSubject" when creating methods that access state or concepts.
### v0.1.69 5/15/2024
* Added priority to axium strategies.
* Improved consistency of logic due the above change.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "stratimux",
"license": "GPL-3.0",
"version": "0.1.69",
"version": "0.1.70",
"description": "Unified Turing Machine",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
4 changes: 3 additions & 1 deletion src/concepts/counter/counter.concept.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { counterAddQuality } from './qualities/add.quality';
import { counterSubtractQuality } from './qualities/subtract.quality';
import { counterSetCountQuality } from './qualities/setCount.quality';
import { createConcept } from '../../model/concept';
import { counterMultiplyQuality } from './qualities/multiply.quality';
export { countingStrategy, primedCountingStrategy } from './strategies/counting.strategy';

export type CounterState = {
Expand All @@ -25,7 +26,8 @@ export const createCounterConcept = () => {
[
counterAddQuality,
counterSubtractQuality,
counterSetCountQuality
counterSetCountQuality,
counterMultiplyQuality
]
);
};
Expand Down
32 changes: 32 additions & 0 deletions src/concepts/counter/qualities/multiply.quality.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*<$
For the asynchronous graph programming framework Stratimux and Counter Concept, generate a quality that will multiply another by an incoming payload
$>*/
/*<#*/
import { defaultMethodCreator } from '../../../model/concept';
import { CounterState } from '../counter.concept';
import { counterSelectCount } from '../counter.selector';
import { createQualitySetWithPayload } from '../../../model/quality';
import { selectPayload } from '../../../model/selector';

type CounterMultiplyPayload = {
by: number;
};

export const [
counterMultiply,
counterMultiplyType,
counterMultiplyQuality
] = createQualitySetWithPayload<CounterMultiplyPayload>({
type: 'Counter Multiply',
reducer: (state: CounterState, action) => {
const {by} = selectPayload<CounterMultiplyPayload>(action);
console.log(state.count, 'by', by);
return {
...state,
count: state.count * by
};
},
methodCreator: defaultMethodCreator,
keyedSelectors: [counterSelectCount]
});
/*#>*/
1 change: 0 additions & 1 deletion src/concepts/ownership/ownership.mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export const ownershipMode: Mode = (
finalMode([nextAction, concepts, action$, concepts$]);
} else {
// This assumes that the Strategy is accounting for the Block
// console.log('Check Action Failed1', action);
[concepts, action] = checkIn(concepts, action);
const nextAction = strategyFailed(
strategy,
Expand Down
1 change: 0 additions & 1 deletion src/concepts/ownership/ownership.principle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export const ownershipPrinciple: PrincipleFunction = (
const plan = concepts$.plan('ownership Principle Plan', [
stageWaitForOpenThenIterate(() => axiumRegisterStagePlanner({conceptName: ownershipName, stagePlanner: plan})),
createStage((cpts, _) => {
console.log('HIT');
let concepts = cpts;
let ownershipState = selectUnifiedState<OwnershipState>(concepts, semaphore);
if (ownershipState?.initialized) {
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export {
createMethodDebounceWithConcepts,
createAsyncMethodThrottleWithConcepts,
createAsyncMethodDebounceWithConcepts,
createMethodBuffer,
createMethodBufferWithState,
createMethodBufferWithConcepts,
createAsyncMethodBuffer,
createAsyncMethodBufferWithConcepts,
createAsyncMethodBufferWithState,
method
} from './model/method';
export {
Expand Down
37 changes: 37 additions & 0 deletions src/model/actionController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,43 @@ export const createActionController$ = (act: Action, controlling: (controller: A
return ctrl;
};

export class ActionControllerForEach extends Subject<Action> {
constructor(actions: Action[]) {
super();
setTimeout(() => {
actions.forEach(action => {
if (action.expiration < Date.now()) {
this.fire(action);
} else if (action.strategy) {
this.fire(
strategyFailed(action.strategy,
strategyData_appendFailure(action.strategy, failureConditions.axiumExpired)));
} else {
this.fire(action);
}
});
this.complete();
}, 0);
}
// next(action: Action[]) {

// }
fire(action: Action) {
if (!this.closed) {
const { observers } = this;
const len = observers.length;
for (let i = 0; i < len; i++) {
observers[i].next(action);
}
}
}
}

export const createActionControllerForEach$ = (acts: Action[]) => {
const ctrl = new ActionControllerForEach(acts);
return ctrl;
};

export const actionController = ({
create$: createActionController$
});
Expand Down
4 changes: 1 addition & 3 deletions src/model/axium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ export function createAxium(
.subscribe(([action, _concepts]: [Action, Concepts]) => {
// Would be notifying methods
const _axiumState = _concepts[0].state as AxiumState;
// console.log('CHECK QUES', _axiumState.head, _axiumState.body, _axiumState.tail);
if (_axiumState.head.length === 0) {
_axiumState.head.push(action);
if (_axiumState.tailTimer.length > 0) {
Expand All @@ -193,11 +192,10 @@ export function createAxium(
}
const modeIndex = _axiumState.modeIndex;
if (getAxiumState(_concepts).logActionStream) {
console.log('CHECK ACTION STREAM', action.type, action.payload, action.semaphore, action.strategy?.topic);
console.log('ACTION STREAM: ', action.type, action.payload, action.semaphore, action.strategy?.topic);
}
const modes = _concepts[0].mode as Mode[];
const mode = modes[modeIndex] as Mode;
// console.log('STREAM', action, mode);
mode([action, _concepts, _axiumState.action$, _axiumState.concepts$]);
_axiumState.head.shift();
if (_axiumState.body.length === 0) {
Expand Down
4 changes: 2 additions & 2 deletions src/model/concept.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type Mode = ([action, concept, action$, concepts$]: [
UnifiedSubject,
]) => void;

export type MethodCreator = (concept$?: Subject<Concepts>, semaphore?: number) => [Method, Subject<Action>];
export type MethodCreator = (concept$: Subject<Concepts>, semaphore: number) => [Method, Subject<Action>];
// export type MethodCreator = (concept$?: UnifiedSubject, semaphore?: number) => [Method, Subject<Action>];

export type Quality = {
Expand Down Expand Up @@ -325,7 +325,7 @@ export const areConceptsLoaded = (concepts: Concepts, conceptNames: string[]): b
return allExists;
};

export const forEachConcept = (concepts: Concepts, each: (concept: Concept, semaphore?: number) => void) => {
export const forEachConcept = (concepts: Concepts, each: (concept: Concept, semaphore: number) => void) => {
const conceptKeys = Object.keys(concepts);
for (const i of conceptKeys) {
const index = Number(i);
Expand Down
147 changes: 145 additions & 2 deletions src/model/method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ This file hold a series of helper functions that enable users to quickly create
within their own defined qualities.
$>*/
/*<#*/
import { Observable, Subject, map, switchMap, withLatestFrom } from 'rxjs';
import { Observable, Subject, bufferTime, filter, map, mergeMap, switchMap, withLatestFrom } from 'rxjs';
import { Concepts } from './concept';
import { ActionController, createActionController$ } from './actionController';
import { ActionController, createActionController$, createActionControllerForEach$ } from './actionController';
import { ActionStrategy } from './actionStrategy';
import { KeyedSelector, selectUnifiedState } from './selector';
import { debounceAction, throttleAction } from './actionOperators';
Expand Down Expand Up @@ -98,6 +98,143 @@ export const createAsyncMethodWithState =
defaultMethod.toString = () => ('Async Method with State');
return [defaultMethod, defaultSubject];
};
export const createMethodBuffer =
(method: (action: Action) => Action, duration: number): [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
const defaultMethod: Method = defaultSubject.pipe(
bufferTime(duration),
filter(actions => actions.length > 0),
switchMap(actions => createActionControllerForEach$(actions)),
map((action: Action) => {
// Logically Determined axiumConclude
if (action.semaphore[3] !== 3) {
const methodAction = method(action);
if (methodAction.strategy) {
return [methodAction, true];
}
const conclude = axiumConclude();
return [{
...action,
...conclude,
}, false];
} else {
return [action, true];
}
}),
);
defaultMethod.toString = () => ('Debounce Method');
return [defaultMethod, defaultSubject];
};
export const createMethodBufferWithState =
<T>(methodWithState:
(action: Action, state: T) => Action, concepts$: Subject<Concepts>, semaphore: number, duration: number): [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
const defaultMethod: Method = defaultSubject.pipe(
bufferTime(duration),
switchMap(actions => createActionControllerForEach$(actions)),
withLatestFrom(concepts$),
map(([act, concepts] : [Action, Concepts]): [Action, T] => ([act, selectUnifiedState<T>(concepts, semaphore) as T])),
map(([act, state] : [Action, T]) => {
// Logically Determined axiumConclude
if (act.semaphore[3] !== 3) {
const methodAction = methodWithState(act, state);
if (methodAction.strategy) {
return [methodAction, true];
}
const conclude = axiumConclude();
return [{
...act,
...conclude,
}, true];
} else {
return [act, true];
}
}),
);
defaultMethod.toString = () => ('Buffer Method with State');
return [defaultMethod, defaultSubject];
};
export const createMethodBufferWithConcepts =
(
methodWithConcepts: (action: Action, concepts: Concepts, semaphore: number) => Action, concepts$: Subject<Concepts>,
semaphore: number,
duration: number
) : [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
const defaultMethod: Method = defaultSubject.pipe(
bufferTime(duration),
switchMap(actions => createActionControllerForEach$(actions)),
withLatestFrom(concepts$),
map(([act, concepts] : [Action, Concepts]) => {
// Logically Determined axiumConclude
if (act.semaphore[3] !== 3) {
const methodAction = methodWithConcepts(act, concepts, semaphore);
if (methodAction.strategy) {
return [methodAction, true];
}
const conclude = axiumConclude();
return [{
...act,
...conclude,
}, false];
} else {
return [act, true];
}
}),
);
defaultMethod.toString = () => ('Buffer Method with Concepts');
return [defaultMethod, defaultSubject];
};
export const createAsyncMethodBuffer =
(asyncMethod: (controller: ActionController, action: Action) => void, duration: number): [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
const defaultMethod: Method = defaultSubject.pipe(
bufferTime(duration),
switchMap(actions => createActionControllerForEach$(actions)),
mergeMap((act) => {
return createActionController$(act, (controller: ActionController, action: Action) => {
asyncMethod(controller, action);
});
}),
);
defaultMethod.toString = () => ('Async Buffer Method');
return [defaultMethod, defaultSubject];
};
export const createAsyncMethodBufferWithState =
<T>(asyncMethodWithState: (controller: ActionController, action: Action, state: T) =>
void, concepts$: Subject<Concepts>, semaphore: number, duration: number, ): [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
const defaultMethod: Method = defaultSubject.pipe(
bufferTime(duration),
switchMap(actions => createActionControllerForEach$(actions)),
withLatestFrom(concepts$),
map(([act, concepts] : [Action, Concepts]): [Action, T] => ([act, selectUnifiedState<T>(concepts, semaphore) as T])),
mergeMap(([act, state] : [Action, T]) => {
return createActionController$(act, (controller: ActionController, action: Action) => {
asyncMethodWithState(controller, action, state);
});
})
);
defaultMethod.toString = () => ('Async Buffer Method with State');
return [defaultMethod, defaultSubject];
};
export const createAsyncMethodBufferWithConcepts =
(asyncMethodWithConcepts: (controller: ActionController, action: Action, concepts: Concepts, semaphore: number) =>
void, concepts$: Subject<Concepts>, semaphore: number, duration: number, ): [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
const defaultMethod: Method = defaultSubject.pipe(
bufferTime(duration),
switchMap(actions => createActionControllerForEach$(actions)),
withLatestFrom(concepts$),
mergeMap(([act, concepts] : [Action, Concepts]) => {
return createActionController$(act, (controller: ActionController, action: Action) => {
asyncMethodWithConcepts(controller, action, concepts, semaphore);
});
}),
);
defaultMethod.toString = () => ('Async Buffer Method with Concepts');
return [defaultMethod, defaultSubject];
};
export const createMethodDebounce =
(method: (action: Action) => Action, duration: number): [Method, Subject<Action>] => {
const defaultSubject = new Subject<Action>();
Expand Down Expand Up @@ -418,5 +555,11 @@ export const method = ({
createAsyncThrottle: createAsyncMethodThrottle,
createAsyncThrottleWithState: createAsyncMethodThrottleWithState,
createAsyncThrottleWithConcepts: createAsyncMethodThrottleWithConcepts,
createBuffer: createMethodBuffer,
createBufferWithState: createMethodBufferWithState,
createMethodBufferWithConcepts: createMethodBufferWithConcepts,
createAsyncBuffer: createAsyncMethodBuffer,
createAsyncBufferWithState: createAsyncMethodBufferWithState,
createAsyncBufferWithConcepts: createAsyncMethodBufferWithConcepts
});
/*#>*/
2 changes: 0 additions & 2 deletions src/model/priority.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Action } from './action';

// Is only called if action has priority
const fillBucket = (body: Action[], bucket: Action[], action: Action, _added = false) => {
// console.log('FILL BUCKET', body, bucket, action, _added);
let added = _added;
const drop = body.shift();
if (drop) {
Expand Down Expand Up @@ -43,7 +42,6 @@ const emptyBucket = (body: Action[], bucket: Action[]) => {

export const handlePriority = (axiumState: AxiumState, action: Action) => {
const body = axiumState.body;
// console.log('HIT HANDLE PRIORITY', body[0],);
if (body[0] && body[0].priority !== undefined) {
const bucket: Action[] = [];
fillBucket(body, bucket, action);
Expand Down
Loading

0 comments on commit 14c0fa5

Please sign in to comment.