Skip to content

Instantly share code, notes, and snippets.

@josephjunker
Last active February 27, 2018 22:38
Show Gist options
  • Save josephjunker/8a7826a856426d13529208cdcfb08ce6 to your computer and use it in GitHub Desktop.
Save josephjunker/8a7826a856426d13529208cdcfb08ce6 to your computer and use it in GitHub Desktop.
Laziness primitive for JS, flow-static-land compatible
// Note that this file still needs testing
// @flow
export opaque type Deferred<A> = Thunk<A> | Pure<A>;
class Suspended<A> {
fn: () => A;
constructor(fn: () => A) {
this.fn = fn;
}
}
class Resolved<A> {
value: A;
constructor(value: A) {
this.value = value;
}
}
class Thunk<A> {
contents: Suspended<A> | Resolved<A>;
constructor(contents: Suspended<A>) {
this.contents = contents;
}
}
class Pure<A> {
value: A;
constructor(value: A) {
this.value = value;
}
}
export function defer<A>(fn: () => A) : Deferred<A> {
return new Thunk(new Suspended(fn));
}
export function force<A>(d: Deferred<A>) : A {
if (d instanceof Pure) return d.value;
if (d.contents instanceof Resolved) return d.contents.value;
const result = d.contents.fn();
d.contents = new Resolved(result);
return result;
}
export function pure<A>(a: A): Deferred<A> {
return new Pure(a);
}
// Note that this file still needs testing
// @flow
import { unsafeCoerce } from "flow-static-land/lib/unsafe";
import { HKT } from "flow-static-land/lib/HKT";
import type { Monad } from "flow-static-land/lib/Monad";
class IsDeferred {}
export type Deferred<A> = HKT<IsDeferred, A>;
type DeferredI<A> = Thunk<A> | Pure<A>;
function inj<A>(deferred: DeferredI<A>) : Deferred<A> {
return unsafeCoerce(deferred);
}
function prj<A>(deferred: Deferred<A>) : DeferredI<A> {
return unsafeCoerce(deferred);
}
class Suspended<A> {
fn: () => A;
constructor(fn: () => A) {
this.fn = fn;
}
}
class Resolved<A> {
value: A;
constructor(value: A) {
this.value = value;
}
}
class Thunk<A> {
contents: Suspended<A> | Resolved<A>;
constructor(contents: Suspended<A>) {
this.contents = contents;
}
}
class Pure<A> {
value: A;
constructor(value: A) {
this.value = value;
}
}
export function defer<A>(fn: () => A) : Deferred<A> {
return inj(new Thunk(new Suspended(fn)));
}
export function force<A>(deferred: Deferred<A>) : A {
const d = prj(deferred);
if (d instanceof Pure) return d.value;
if (d.contents instanceof Resolved) return d.contents.value;
const result = d.contents.fn();
d.contents = new Resolved(result);
return result;
}
export function map<A, B>(fn: A => B, deferred: Deferred<A>) : Deferred<B> {
return defer(() => fn(force(deferred)));
}
export function of<A>(a: A): Deferred<A> {
return inj(new Pure(a));
}
export function ap<A, B>(deferredFn: Deferred<A => B>, deferred: Deferred<A>) : Deferred<B> {
return defer(() => force(deferredFn)(force(deferred)));
}
export function chain<A, B>(fn: A => Deferred<B>, deferred: Deferred<A>): Deferred<B> {
return defer(() => force(fn(force(deferred))));
}
export const deferred = {
map,
of,
ap,
chain
};
if (false) {
(deferred : Monad<IsDeferred>);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment