Last active
May 4, 2020 14:15
-
-
Save Lucifier129/65b7eac1ad7895c91c8b10bcbe83c7af to your computer and use it in GitHub Desktop.
functional oop
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// option type | |
interface OptionVisitor<A, B> { | |
None: () => B; | |
Some: (a: A) => B; | |
} | |
type OptionData<A> = | |
| { | |
kind: 'None'; | |
} | |
| { | |
kind: 'Some'; | |
value: A; | |
}; | |
interface Option<A> { | |
case: <B>(visitor: OptionVisitor<A, B>) => B; | |
map: <B>(f: (a: A) => B) => Option<B>; | |
data: () => OptionData<A>; | |
} | |
const fromOptionData = <T>(data: OptionData<T>): Option<T> => { | |
if (data.kind === 'None') { | |
return None(); | |
} | |
if (data.kind === 'Some') { | |
return Some(data.value); | |
} | |
}; | |
// data constructors of option type | |
const None = <A = any>(): Option<A> => { | |
return { | |
case: (visitor) => { | |
return visitor.None(); | |
}, | |
map: () => { | |
return None(); | |
}, | |
data: () => { | |
return { | |
kind: 'None', | |
}; | |
}, | |
}; | |
}; | |
const Some = <A>(a: A): Option<A> => { | |
return { | |
case: (visitor) => { | |
return visitor.Some(a); | |
}, | |
map: (f) => { | |
return Some(f(a)); | |
}, | |
data: () => { | |
return { | |
kind: 'Some', | |
value: a, | |
}; | |
}, | |
}; | |
}; | |
// result type | |
interface ResultVisitor<A, B, T> { | |
Ok: (a: A) => T; | |
Err: (b: B) => T; | |
} | |
type ResultData<A, B> = { kind: 'Ok'; value: A } | { kind: 'Err'; value: B }; | |
interface Result<A, B> { | |
case: <T>(visitor: ResultVisitor<A, B, T>) => T; | |
map: <T>(f: (a: A) => T) => Result<T, B>; | |
data: () => ResultData<A, B>; | |
} | |
// data constructors of result type | |
const fromResultData = <A, B>(data: ResultData<A, B>): Result<A, B> => { | |
switch (data.kind) { | |
case 'Ok': | |
return Ok(data.value); | |
case 'Err': | |
return Err(data.value); | |
} | |
}; | |
const Ok = <A>(a: A): Result<A, any> => { | |
return { | |
case: (visitor) => { | |
return visitor.Ok(a); | |
}, | |
map: (f) => { | |
return Ok(f(a)); | |
}, | |
data: () => { | |
return { | |
kind: 'Ok', | |
value: a, | |
}; | |
}, | |
}; | |
}; | |
const Err = <B>(b: B): Result<any, B> => { | |
return { | |
case: (visitor) => { | |
return visitor.Err(b); | |
}, | |
map: () => { | |
return Err(b); | |
}, | |
data: () => { | |
return { | |
kind: 'Err', | |
value: b, | |
}; | |
}, | |
}; | |
}; | |
// tree type | |
interface TreeVisitor<T, TT> { | |
node: (value: T, ...children: Tree<T>[]) => TT; | |
} | |
interface TreeData<T> { | |
kind: 'Node'; | |
value: T; | |
children: TreeData<T>[]; | |
} | |
interface Tree<T> { | |
case: <TT>(visitor: TreeVisitor<T, TT>) => TT; | |
map: <TT>(f: (a: T) => TT) => Tree<TT>; | |
data: () => TreeData<T>; | |
} | |
// data constructors of tree type | |
const fromTreeData = <T>(data: TreeData<T>): Tree<T> => { | |
if (data.kind === 'Node') { | |
return node(data.value, ...data.children.map(fromTreeData)); | |
} | |
}; | |
const node = <T>(a: T, ...children: Tree<T>[]): Tree<T> => { | |
return { | |
case: (visitor) => { | |
return visitor.node(a, ...children); | |
}, | |
map: (f) => { | |
let newValue = f(a); | |
let newChildren = children.map((child) => child.map(f)); | |
return node(newValue, ...newChildren); | |
}, | |
data: () => { | |
return { | |
kind: 'Node', | |
value: a, | |
children: children.map((child) => child.data()), | |
}; | |
}, | |
}; | |
}; | |
// test | |
const incre = (n: number) => n + 1; | |
const toCount = (n: number) => `count: ${n}`; | |
const t1 = None().map(incre).map(toCount).data(); | |
const t2 = Some(1).map(incre).map(toCount).data(); | |
const t3 = Ok(2).map(incre).map(toCount).data(); | |
const t4 = Err('you bad bad').map(incre).map(toCount).data(); | |
const tree0 = node(1, node(2, node(3), node(4), node(5)), node(6)); | |
const tree1 = tree0.map(incre).map(toCount); | |
type TT = [typeof tree0, typeof tree1]; | |
console.log({ | |
t1, | |
t2, | |
t3, | |
t4, | |
}); | |
console.log('tree0', JSON.stringify(tree0.data(), null, 2)); | |
console.log('tree1', JSON.stringify(tree1.data(), null, 2)); | |
console.log( | |
'equality testing of tree0', | |
JSON.stringify(tree0.data()) === | |
JSON.stringify(fromTreeData(tree0.data()).data()) | |
); | |
console.log( | |
'equality testing of tree1', | |
JSON.stringify(tree1.data()) === | |
JSON.stringify(fromTreeData(tree1.data()).data()) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
todos