Skip to content

Instantly share code, notes, and snippets.

@mattiamanzati
Created June 19, 2018 08:41
Show Gist options
  • Save mattiamanzati/a4807e19e4455be9630b2dc4835ef9d9 to your computer and use it in GitHub Desktop.
Save mattiamanzati/a4807e19e4455be9630b2dc4835ef9d9 to your computer and use it in GitHub Desktop.
import * as t from "io-ts"
import {taskEither, TaskEither} from "fp-ts/lib/TaskEither"
import {Either, either} from "fp-ts/lib/Either"
import { Task } from 'fp-ts/lib/Task';
export type IFieldObserver<T> = (newValue: T) => TaskEither<any, void>
export type IFieldInterceptor<T> = (newValue: T) => TaskEither<any, T>
export interface IDisposer extends TaskEither<void, void>{}
export class Ref<T>{
protected readonly observers: IFieldObserver<T>[] = []
protected readonly interceptors: IFieldInterceptor<T>[] = []
constructor(protected storedValue: T) {
}
read() {
return this.storedValue
}
write(newValue: T) {
return this.observers.reduce(
(task, observer) => task.chain((v) => observer(v).map(() => v)),
this.interceptors.reduce(
(task, interceptor) => task.chain(interceptor),
taskEither.of(newValue)
).chain((currentValue) => new TaskEither(new Task(() => {
this.storedValue = currentValue
return Promise.resolve(either.of(currentValue))
}))
).map(() => { })
}
observe(fn: IFieldObserver<T>) {
return new TaskEither(new Task(() => {
let isSubscribed = true
this.observers.push(fn)
return Promise.resolve(
either.of(new TaskEither<void, void>(new Task(() => {
if (isSubscribed) {
this.observers.splice(this.observers.indexOf(fn), 1)
isSubscribed = false
}
return Promise.resolve(either.of(undefined))
})))
)
}))
}
intercept(fn: IFieldInterceptor<T>) {
return new TaskEither(new Task(() => {
let isSubscribed = true
this.interceptors.push(fn)
return Promise.resolve(
either.of(new TaskEither<void, void>(new Task(() => {
if (isSubscribed) {
this.interceptors.splice(this.interceptors.indexOf(fn), 1)
isSubscribed = false
}
return Promise.resolve(either.of(undefined))
})))
)
}))
}
}
export class RefType<A, O, I> extends t.Type<Ref<A>, O, I>{
constructor(
type: t.Type<A, O, I>
){
super(
`Ref<${type.name}>`,
(m): m is Ref<A> => type.is(m),
(m, c) => this.is(m) ? type.validate(m, c).map(v => new Ref(v)) : t.failure(m, c),
(m) => type.encode(m.read())
)
}
}
function ref<A, O, I>(type: t.Type<A, O, I>){
return new RefType(type)
}
function entity<P extends t.AnyProps>(props: P) {
return t.readonly(t.interface(props))
}
export const types = {
string: t.string,
boolean: t.boolean,
number: t.number,
Integer: t.Integer,
ref,
entity
}
import {types} from "./data"
const User = types.entity({
id: types.number,
name: types.ref(types.string)
})
// vorrei che questo
const program = User.decode({
id: 1,
name: "World"
}).map(
john => john.name.write("John Smith") // => TaskEither<WriteError, void>
.map((l) => {
console.log("New name is", john.name.read()) // => void
return l
})
)
// diventasse questo
new Scope()
.let("john", () => User.decode({
id: 1,
name: "World"
}))
.do(s => s.john.name.write("John Smith"))
.do(s => console.log("New name is", john.name.read()))
.return(s => s.john) // TaskEither<ValidationError | WriteError, t.TypeOf<typeof john>>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment