Skip to content

Commit

Permalink
feat(fio): add support for environment access
Browse files Browse the repository at this point in the history
  • Loading branch information
tusharmath committed Jun 18, 2019
1 parent e58d43d commit 89e0387
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 76 deletions.
16 changes: 15 additions & 1 deletion src/internals/Evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const Evaluate = <E, A>(
rej: CB<E>,
res: CB<A>
): void => {
const {stackA, stackE, cancellationList, sh} = context
const {stackA, stackE, stackEnv, cancellationList, sh} = context
let data: unknown

while (true) {
Expand Down Expand Up @@ -73,6 +73,20 @@ export const Evaluate = <E, A>(
data = nContext
break

case Tag.Provide:
stackA.push(j.i0)
stackEnv.push(j.i1)
break

case Tag.Environment:
data = stackEnv.pop()
break

case Tag.Access:
const env = stackEnv.pop()
data = j.i0(env)
break

case Tag.Async:
const id = cancellationList.push(
j.i0(
Expand Down
28 changes: 14 additions & 14 deletions src/internals/FiberContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,22 @@ import {Evaluate} from './Evaluate'
*/
export class FiberContext<E = never, A = never> extends Fiber<E, A>
implements ICancellable {
/**
* Pure implementation of cancel()
*/
public get abort(): UIO<void> {
return FIO.uio(() => this.$abort())
}

/**
* Pure implementation of $resume().
*/
public get resume(): FIO<E, A> {
return FIO.asyncIO<E, A>((rej, res) => this.$resume(rej, res))
}
public readonly stackA: Instruction[] = []
public readonly stackE: Array<(e: unknown) => Instruction> = []
public readonly stackEnv: unknown[] = []

public constructor(
public readonly sh: IScheduler,
Expand Down Expand Up @@ -68,20 +82,6 @@ export class FiberContext<E = never, A = never> extends Fiber<E, A>
return this
}

/**
* Pure implementation of cancel()
*/
public get abort(): UIO<void> {
return FIO.uio(() => this.$abort())
}

/**
* Pure implementation of $resume().
*/
public get resume(): FIO<E, A> {
return FIO.asyncIO<E, A>((rej, res) => this.$resume(rej, res))
}

public resumeAsync(cb: (exit: Exit<E, A>) => UIO<void>): UIO<Fiber<E, A>> {
const eee = <X>(con: (x: X) => Exit<E, A>) => (data: X) => {
// tslint:disable-next-line: no-use-before-declare
Expand Down
112 changes: 73 additions & 39 deletions src/main/FIO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,46 @@ import {Fiber} from './Fiber'
import {Instruction, Tag} from './Instructions'
import {Ref} from './Ref'

//#region Helper Functions
const Id = <A>(_: A): A => _
const ExitRef = <E = never, A = never>() => Ref.of<Exit<E, A>>(Exit.pending)
//#endregion

type AA<A1, A2> = A1 & A2 extends never ? never : [A1, A2]
type iRR<R1, R2> = R1 & R2 extends never ? R1 | R2 : R1 & R2
export type NoEnv = never
type iAA<A1, A2> = A1 & A2 extends never ? never : [A1, A2]

export type Task<A> = FIO<Error, A>
export type UIO<A> = FIO<never, A>
export type IO<E, A> = FIO<E, A>
export type Task<A> = IO<Error, A>
export type UIO<A> = IO<never, A>

export class FIO<E1 = unknown, A1 = unknown> {
export class FIO<E1 = unknown, A1 = unknown, R1 = NoEnv> {
public get asInstruction(): Instruction {
return this as Instruction
}

public get fork(): FIO<never, Fiber<E1, A1>> {
public get fork(): FIO<never, Fiber<E1, A1>, R1> {
return new FIO(Tag.Fork, this)
}

public get once(): FIO<never, FIO<E1, A1>> {
return Await.of<E1, A1>().map(await => await.set(this).and(await.get))
public get env(): FIO<never, R1, R1> {
return FIO.environment<R1>()
}

public get void(): FIO<E1, void> {
public get once(): FIO<never, FIO<E1, A1>, R1> {
return this.env.chain(env =>
Await.of<E1, A1>().map(await =>
await.set(this.provide(env)).and(await.get)
)
)
}

public get void(): FIO<E1, void, R1> {
return this.const(undefined)
}

public static access<R, A>(cb: (R: R) => A): FIO<never, A, R> {
return new FIO(Tag.Access, cb)
}

/**
* **NOTE:** The default type is set to `never` because it hard for typescript to infer the types based on how we use `res`.
* Using `never` will give users compile time error always while using.
Expand Down Expand Up @@ -67,17 +80,17 @@ export class FIO<E1 = unknown, A1 = unknown> {
return FIO.async((rej, res, sh) => cb(res, sh))
}

public static catch<E1, A1, E2, A2>(
fa: FIO<E1, A1>,
aFe: (e: E1) => FIO<E2, A2>
): FIO<E2, A2> {
public static catch<E1, A1, R1, E2, A2, R2>(
fa: FIO<E1, A1, R1>,
aFe: (e: E1) => FIO<E2, A2, R2>
): FIO<E2, A2, iRR<R1, R2>> {
return new FIO(Tag.Catch, fa, aFe)
}

public static chain<E1, A1, E2, A2>(
fa: FIO<E1, A1>,
aFb: (a: A1) => FIO<E2, A2>
): FIO<E1 | E2, A2> {
public static chain<E1, A1, R1, E2, A2, R2>(
fa: FIO<E1, A1, R1>,
aFb: (a: A1) => FIO<E2, A2, R2>
): FIO<E1 | E2, A2, iRR<R1, R2>> {
return new FIO(Tag.Chain, fa, aFb)
}

Expand All @@ -104,6 +117,16 @@ export class FIO<E1 = unknown, A1 = unknown> {
)
}

public static environment<R1 = never>(): FIO<never, R1, R1> {
return new FIO(Tag.Environment)
}

public static flatten<E1, A1, R1, E2, A2, R2>(
fio: FIO<E1, FIO<E2, A2, R2>, R1>
): FIO<E1 | E2, A2, iRR<R1, R2>> {
return fio.chain(Id)
}

public static fromExit<E, A>(exit: Exit<E, A>): FIO<E, A> {
return Exit.isSuccess(exit)
? FIO.of(exit[1])
Expand All @@ -116,10 +139,10 @@ export class FIO<E1 = unknown, A1 = unknown> {
return FIO.resume(cb)
}

public static map<E1, A1, A2>(
fa: FIO<E1, A1>,
public static map<E1, A1, R1, A2>(
fa: FIO<E1, A1, R1>,
ab: (a: A1) => A2
): FIO<E1, A2> {
): FIO<E1, A2, R1> {
return new FIO(Tag.Map, fa, ab)
}

Expand Down Expand Up @@ -180,35 +203,40 @@ export class FIO<E1 = unknown, A1 = unknown> {
public readonly i1?: unknown
) {}

public and<E2, A2>(aFb: FIO<E2, A2>): FIO<E1 | E2, A2> {
public and<E2, A2, R2>(aFb: FIO<E2, A2, R2>): FIO<E1 | E2, A2, iRR<R1, R2>> {
return this.chain(() => aFb)
}

public catch<E2, A2>(aFb: (e: E1) => FIO<E2, A2>): FIO<E2, A1 | A2> {
public catch<E2, A2, R2>(
aFb: (e: E1) => FIO<E2, A2, R2>
): FIO<E2, A1 | A2, iRR<R1, R2>> {
return FIO.catch(this, aFb)
}

public chain<E2, A2>(aFb: (a: A1) => FIO<E2, A2>): FIO<E1 | E2, A2> {
public chain<E2, A2, R2>(
aFb: (a: A1) => FIO<E2, A2, R2>
): FIO<E1 | E2, A2, iRR<R1, R2>> {
return FIO.chain(this, aFb)
}

public const<A2>(a: A2): FIO<E1, A2> {
public const<A2>(a: A2): FIO<E1, A2, R1> {
return this.and(FIO.of(a))
}

public delay(duration: number): FIO<E1, A1> {
public delay(duration: number): FIO<E1, A1, R1> {
return FIO.timeout(this, duration).chain(Id)
}

public map<A2>(ab: (a: A1) => A2): FIO<E1, A2> {
public map<A2>(ab: (a: A1) => A2): FIO<E1, A2, R1> {
return FIO.map(this, ab)
}
public provide = (r1: R1): FIO<E1, A1> => new FIO(Tag.Provide, this, r1)

public raceWith<E2, A2>(
that: FIO<E2, A2>,
public raceWith<E2, A2, R2>(
that: FIO<E2, A2, R2>,
cb1: (exit: Exit<E1, A1>, fiber: Fiber<E2, A2>) => UIO<void>,
cb2: (exit: Exit<E2, A2>, fiber: Fiber<E1, A1>) => UIO<void>
): FIO<never, void> {
): FIO<never, void, iRR<R1, R2>> {
return this.fork.zip(that.fork).chain(([f1, f2]) => {
const resume1 = f1.resumeAsync(exit => cb1(exit, f2))
const resume2 = f2.resumeAsync(exit => cb2(exit, f1))
Expand All @@ -217,25 +245,31 @@ export class FIO<E1 = unknown, A1 = unknown> {
})
}

public tap(io: UIO<void>): FIO<E1, A1> {
public tap(io: UIO<void>): FIO<E1, A1, R1> {
return this.chain(_ => io.const(_))
}

public zip<E2, A2>(that: FIO<E2, A2>): FIO<E1 | E2, AA<A1, A2>> {
return this.zipWith(that, (a, b) => [a, b]) as FIO<E1 | E2, AA<A1, A2>>
public zip<E2, A2, R2>(
that: FIO<E2, A2, R2>
): FIO<E1 | E2, iAA<A1, A2>, iRR<R1, R2>> {
return this.zipWith(that, (a, b) => [a, b]) as FIO<
E1 | E2,
iAA<A1, A2>,
iRR<R1, R2>
>
}

public zipWith<E2, A2, C>(
that: FIO<E2, A2>,
public zipWith<E2, A2, R2, C>(
that: FIO<E2, A2, R2>,
c: (a1: A1, a2: A2) => C
): FIO<E1 | E2, C> {
): FIO<E1 | E2, C, iRR<R1, R2>> {
return this.chain(a1 => that.map(a2 => c(a1, a2)))
}

public zipWithPar<E2, A2, C>(
that: FIO<E2, A2>,
public zipWithPar<E2, A2, R2, C>(
that: FIO<E2, A2, R2>,
c: (e1: Exit<E1, A1>, e2: Exit<E2, A2>) => C
): FIO<void, C> {
): FIO<void, C, iRR<R1, R2>> {
// Create Caches
const cache = ExitRef<E1, A1>().zip(ExitRef<E2, A2>())

Expand Down
15 changes: 14 additions & 1 deletion src/main/Instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ export enum Tag {
Catch,
Chain,
Constant,
Environment,
Fork,
Map,
Never,
Provide,
Reject,
Fork,
Try,
TryM
}
Expand Down Expand Up @@ -65,6 +67,15 @@ export interface IAccess<X = unknown, Y = unknown> {
tag: Tag.Access
i0(X: X): Y
}
export interface IProvide<R = unknown> {
i0: Instruction
i1: R
tag: Tag.Provide
}

export interface IEnvironment {
tag: Tag.Environment
}

/**
* @ignore
Expand All @@ -75,9 +86,11 @@ export type Instruction =
| ICatch
| IChain
| IConstant
| IEnvironment
| IFork
| IMap
| INever
| IProvide
| IReject
| ITry
| ITryM
28 changes: 7 additions & 21 deletions test/FIO.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,13 @@ describe('FIO', () => {
})

describe('access', () => {
it.skip('should access a value and transform')
})

describe('accessM', () => {
it.skip('should purely access the env')
})

describe('chain', () => {
it.skip('should sequence the operations')
it('should access a value and transform', () => {
const actual = testRuntime().executeSync(
FIO.access((name: string) => name.length).provide('FIO')
)
const expected = 3
assert.strictEqual(actual, expected)
})
})

describe('reject', () => {
Expand Down Expand Up @@ -75,10 +73,6 @@ describe('FIO', () => {
})
})

describe('accessP', () => {
it.skip('should access promise based envs')
})

describe('try', () => {
it('should call the cb function', () => {
let i = 1000
Expand Down Expand Up @@ -299,8 +293,6 @@ describe('FIO', () => {
assert.strictEqual(runtime.scheduler.now(), 1101)
})

it.skip('should bubble the env')

it('should resolve after the IO is completed', () => {
const counter = new Counter()
const runtime = testRuntime()
Expand Down Expand Up @@ -409,12 +401,6 @@ describe('FIO', () => {
})
})

describe('provide', () => {
it.skip('should provide the env to FIO')

it.skip('should maintain order')
})

describe('zipWith', () => {
it('should sequentially combine two FIO', () => {
const actual = testRuntime().executeSync(
Expand Down

0 comments on commit 89e0387

Please sign in to comment.