Skip to content

Instantly share code, notes, and snippets.

@mattiamanzati
Created February 14, 2018 13:42
Show Gist options
  • Save mattiamanzati/788a2989acbff9aab5dde42f7c043919 to your computer and use it in GitHub Desktop.
Save mattiamanzati/788a2989acbff9aab5dde42f7c043919 to your computer and use it in GitHub Desktop.
import { Task, task, sequence, array } from "./fp-ts"
import { identity } from "fp-ts/lib/function";
/**
* Gli `Scope` sono strutture di supporto per l'esecuzione sequenziale di `Task` asincroni.
*
* Lo scope si inizializza, si compone di una serie di `let`, `for` e `do` e termina la propria vita a seguito di un `return`.
* Tutte le istruzioni dello `Scope` sono garantite di essere eseguite sequenzialmente.
*/
export interface IScope<T> {
/**
* Aggiunge o reimposta nello scope attuale il valore dell'indice `varName`.
* Il valore deve essere un Task, che quindi viene calcolato in maniera asincrona.
* ```
* new Scope()
* .let("x", () => task.of(1))
* .let("y", () => task.of(2))
* .return(scope => scope.x + scope.y) // => Task<number>
* ```
*/
let<K extends string, J>(varName: K, fn: (scope: T) => Task<J>): IScope<T & {readonly [JK in K]: J}>
/**
* Data una una funzione che ritorna una array di task, li esegue uno ad uno sequenzialmente,
* e setta il risultato di ogni task in una array dello scope sotto l'indice `varName` indicato.
* ```
* new Scope()
* .for("x", scope => [task.of(1), task.of(2), task.of(3)])
* .return(scope => scope.x) // => Task<number[]>
* ```
* @param varName L'indice in cui inserire l'array di risultato
* @param fn La funzione che ritorna una array di `Task` da eseguire sequenzialmente.
*/
for<K extends string, J>(varName: K, fn: (scope: T) => ReadonlyArray<Task<J>>): IScope<T & {readonly [JK in K]: ReadonlyArray<J>}>
/**
* Esegue sequenzialmente una computazione asincrona senza variare lo scope attuale.
* ```
* new Scope()
* .for("x", () => task.of(1))
* .do(scope => delay(scope.x * 1000))
* .return(scope => scope.x) // => Task<number>
* ```
*/
do(fn: (scope: T) => Task<void>): IScope<T>
/**
* Converte lo `Scope` in un `Task` che si risolve con il valore di ritorno della funzione passata.
* ```
* new Scope()
* .for("x", () => task.of(1))
* .return(scope => scope.x) // => Task<number>
* ```
*/
return<J>(fn: (scope: T) => J): Task<J>
/**
*
* @param varName Nome dell'indice dello Scope contenente un booleano
* @param truthy Istruzione da eseguire sullo scope se vero
* @param falsy Istruzione da eseguire sullo scope se falso
*/
if<K extends keyof T, L, R>(varName: K, truthy: (scope: IScope<T>) => IScope<T & R>, falsy?: (scope: IScope<T>) => IScope<T & L>): IScope<T & (
(L & ({[TK in K]: false})) |
(R & ({[TK in K]: true}))
)>
}
const seq = sequence(task, array)
export class Scope<T extends Object = {}> implements IScope<T> {
/**
* Inizializza un nuovo scope.
* @param _task Il task che inizializza il valore dello scope corrente, se omesso, uno vuoto verrà fornito.
*/
constructor(protected readonly _task: Task<T> = task.of({}) as any) {
}
for<K extends string, J>(varName: K, fn: (scope: T) => Task<J>[]): IScope<T & {readonly [JK in K]: ReadonlyArray<J>}> {
return new Scope(this._task.chain(scope => seq(fn(scope)).map(value => Object.assign({}, scope, { [varName]: value })))) as any
}
let<K extends string, J>(varName: K, fn: (scope: T) => Task<J>): IScope<T & {readonly [JK in K]: J}> {
return new Scope(this._task.chain(scope => fn(scope).map(value => Object.assign({}, scope, { [varName]: value })))) as any
}
do(fn: (scope: T) => Task<void>): IScope<T> {
return new Scope(this._task.chain(scope => fn(scope).map(() => scope)))
}
return<J>(fn: (scope: T) => J): Task<J> {
return this._task.map(fn)
}
if<K extends keyof T, L, R>(varName: K, truthy: (scope: IScope<T>) => IScope<T & R>, falsy?: (scope: IScope<T>) => IScope<T & L>): IScope<T & (
(L & ({[TK in K]: false})) |
(R & ({[TK in K]: true}))
)> {
const realFalsy = falsy ? falsy : (scope: IScope<T>) => scope
return new Scope(this._task.chain(scope => scope[varName] ? truthy(this).return<any>(s => s) : realFalsy(this).return<any>(s => s)))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment