Skip to content

Instantly share code, notes, and snippets.

@mrange
Last active October 1, 2017 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrange/f1d33cc14874b3a283a5691effc02847 to your computer and use it in GitHub Desktop.
Save mrange/f1d33cc14874b3a283a5691effc02847 to your computer and use it in GitHub Desktop.
namespace AnyTransformer {
export interface Cons<T> {
kind: "Cons"
head: T
tail: ImmutableList<T>
}
export interface Empty {
kind: "Empty"
}
export type ImmutableList<T> = Cons<T> | Empty
export let empty : Empty = { kind: "Empty" }
export function cons<T>(head: T, tail: ImmutableList<T>) : ImmutableList<T> {
return {
kind: "Cons" ,
head: head ,
tail: tail
}
}
export interface BadCause {
message: string
}
function badCauseIndexOutOfRange(index: number): BadCause {
return {
message: "IndexOutOfRange: " + index
}
}
function badCauseMessage(msg: string) : BadCause {
return {
message: "Message: " + msg
}
}
function badCauseNotAnArray(): BadCause {
return {
message: "NotAnObject"
}
}
function badCauseNotAnObject(): BadCause {
return {
message: "NotAnObject"
}
}
function badCauseMissingMember(name: string): BadCause {
return {
message: "MissingMember:" + name
}
}
export interface MemberPathPart {
kind : "Member"
member: string
}
export interface IndexPathPart {
kind : "Index"
index : number
}
export interface NamedPathPart {
kind: "Named"
name: string
}
export type PathPart = MemberPathPart | IndexPathPart | NamedPathPart
function indexPathPart(index: number): IndexPathPart {
return {
kind : "Index",
index : index
}
}
function memberPathPart(name: string) : MemberPathPart {
return {
kind : "Member" ,
member: name
}
}
export type Path = ImmutableList<PathPart>
export interface Leaf {
kind : "Leaf"
path : Path
badCause : BadCause
}
export interface Fork {
kind : "Fork"
left : BadTree
right : BadTree
}
export interface Suppress {
kind : "Suppress"
tree : BadTree
}
export type BadTree = Empty | Leaf | Fork | Suppress
function btleaf(path: Path, badCause: BadCause): Leaf {
return {
kind : "Leaf" ,
path : path ,
badCause: badCause
}
}
function btfork(l: BadTree, r: BadTree) : Fork {
return {
kind : "Fork",
left : l ,
right : r
}
}
function btsuppress(t: BadTree): Suppress {
return {
kind: "Suppress",
tree: t
}
}
function btjoin(l: BadTree, r: BadTree) : BadTree {
if (l.kind == "Empty") return r
else if (r.kind == "Empty") return l
else if (l.kind == "Suppress" && r.kind == "Suppress") return btsuppress(btfork(l.tree, r.tree))
else return btfork(l, r)
}
export interface TransformResult<T> {
value : T
badTree : BadTree
}
export interface Transform<T> {
(path: Path, value: any) : TransformResult<T>
}
export function tresult<T>(value: T, badTree: BadTree) : TransformResult<T> {
return {
value: value ,
badTree: badTree
}
}
export function trgood<T>(value: T) {
return tresult<T>(value, empty)
}
export function treturn<T>(goodValue: T) : Transform<T> {
return (path, value) => {
return trgood(goodValue)
}
}
export function tbad<T>(badValue: T, badCause: BadCause) : Transform<T> {
return (path, value) => {
return tresult(badValue, btleaf(path, badCause))
}
}
export function tbind<T, U>(t: Transform<T>, uf: (T) => Transform<U> ): Transform<U> {
return (path, value) => {
let tr = t(path, value)
let u = uf(tr.value)
let ur = u(path, value)
return tresult(ur.value, btjoin(tr.badTree, ur.badTree))
}
}
export function tapply<T, U>(f: Transform<(T) => U>, t: Transform<T> ): Transform<U> {
return (path, value) => {
let fr = f(path, value)
let tr = t(path, value)
return tresult(fr.value(tr.value), btjoin(fr.badTree, tr.badTree))
}
}
export function tmap<T, U>(m: (T) => U, t: Transform<T>): Transform<U> {
return (path, value) => {
let tr = t(path, value)
return tresult(m(tr.value), tr.badTree)
}
}
export function tindex<T>(index: number, badValue: T, t: Transform<T>): Transform<T> {
return (path, value) => {
if (value instanceof Array) {
if (index >= 0 && index < value.length) {
let nv = value[index]
let np = cons(indexPathPart(index), path)
t(np, nv)
} else {
return tresult(badValue, btleaf(path, badCauseIndexOutOfRange(name)))
}
} else {
return tresult(badValue, btleaf(path, badCauseNotAnArray()))
}
}
}
export function tmember<T>(name: string, badValue: T, t: Transform<T>): Transform<T> {
return (path, value) => {
if (value instanceof Object) {
let nv = value[name]
if (nv != null) {
let np = cons(memberPathPart(name), path)
t(np, nv)
} else {
return tresult(badValue, btleaf(path, badCauseMissingMember(name)))
}
} else {
return tresult(badValue, btleaf(path, badCauseNotAnObject()))
}
}
}
export function tasString(): Transform<string> {
return (path, value) => {
if (value == null) {
return tresult("", empty)
} else if (value instanceof String) {
return tresult(<string>value, empty)
} else {
return tresult(value.toString(), empty)
}
}
}
export function tasNumber(): Transform<number> {
return (path, value) => {
if (value == null) {
return tresult(0.0, empty)
} else if (value instanceof Number) {
return tresult(<number>value, empty)
} else {
let n = Number(value)
if (isNaN(n)) {
return tresult(n, btleaf(path, badCauseMessage("Can't interpret value as number")))
} else {
return tresult(n, empty)
}
}
}
}
export function trun<T>(t: Transform<T>, value: any) : [T, BadTree] {
let tr = t(empty, value)
return [tr.value, tr.badTree]
}
}
let customer =
{
envelopeId: "uuid1001" ,
timestamp : "2017-10-01" ,
schema : "new_customer" ,
request :
{
id : 1001 ,
firstName : "Bill" ,
lastName : "Gates"
}
}
let t = AnyTransformer.tasNumber()
document.body.innerHTML = AnyTransformer.trun(transf, "1.0").toString()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment