// Array 도 일종의 functor 의 개념
const first = [1,2,3].map((x:number)=>x) // array
const second = first.map((x:number)=>x) // array
obj.map().map().map() …
class Wrapper{
constructor(value){
this._value = value
}
map(f){
return f(this._value)
}
}
const wrappedValue = new Wrapper('Wrapper')
wrappedValue.map(console.log) // "Wrapper"
wrappedValue.map((x)=>(x.toUpperCase()) // "WRAPPER"
of
map
join
or flatMap
nested 자료구조를 단순하게 만드는 것
class ExampleMonad {
constructor(value){
this._value = value
}
of(value){ // 단위 함수
return new ExampleMonad(value)
}
map(f){ // 바인드 함수
return ExampleMonad.of(f(this._value))
}
join(){ // 조인 연산
return this._value
}
}
const example = ExampleMonad.of('value')
const example2 = example.map((val)=> val.toUpperCase())
// ExampleMonad 객체가 return
one or none
one or the other, but not both
Option
으로도 많이 쓰임)export class MayBe {
_value: any;
constructor(value: any) {
this._value = value;
}
static of(value: any) {
return new MayBe(value);
}
isNothing() {
return this._value !== null;
}
map(f: CallableFunction) {
return this.isNothing() ? MayBe.of(f(this._value)) : MayBe.of(null);
}
join() {
return this.isNothing() ? MayBe.of(null) : this._value;
}
chain(f: CallableFunction) {
return this.map(f).join();
}
}
let res = Maybe.of('George')
.map((x: string) => x.toUpperCase())
.map((x: string) => 'Mr. ' + x);
// MayBe { value: 'Mr. GEORGE' }
res = Maybe.of('George')
.map(() => undefined)
.map((x: string) => 'Mr. ' + x);
// MayBe { value : null }
res = Maybe.of('George')
.map(() => undefined) // A funtion
.map((x: string) => x)
.map(() => undefined) // B function
.map((x: string) => 'Mr. ' + x);
// MayBe { value : null }
export class Left extends Either{
get value(){
throw new Error('no value')
}
getOrElse(other:any){
return other
}
getOrElseThrow(other:any){
throw new Error(other)
}
map(_f:CallableFunction){
return this
}
}
export class Right extends Either{
get value(){
return this._value
}
getOrElse(_other:any){
return this._value
}
getOrElseThrow(other){
return this._value
}
map(f:CallableFunction){
return Either.of(f(this._value))
}
}
export class Either {
_value:any
constructor(value:any){
this._value = value
}
static of(value:any){
return Either.right(value)
}
static left(value:any){
return new Left(value)
}
static right(value:any){
return new Right(value)
}
static fromNullable(value:any){
return value !== null && value !== undefined ? Either.right(value) : Either.left(value)
}
}
const identity = (value: any) => Either.fromNullable(value).map((x: any) => x);
const res = identity('init-value').getOrElseThrow('error is occur') // init-value
const error = identity(null).getOrElseThrow('null is given') // new Error('null is given')
개념적인 부분은 위와 같다. 하지만 개념적인 부분과 실제사용은 확실히 다른법,
실제사용부분에 대해서 다음 글에서 다루도록 하겠다.
const validData = E.right({ msg: 'valid data' });
const wrongData = E.left({
msg: 'error occur',
error: new Error('some error'),
});
pipe(
validData, // wrongData
E.map((data: any) => {
data['some'] = 'real';
console.log('first process', data);
return data;
}),
E.map((data) => {
console.log('middle process', data);
return data;
}),
E.match(
(e) => {
console.log(`warning, error occur!`);
return e;
},
(data) => data
),
(data) => console.log('result is', data)
);
console.log
first process { msg: 'valid data', some: 'real' }
console.log
middle process { msg: 'valid data', some: 'real' }
console.log
result is { msg: 'valid data', some: 'real' }
console.log // error 가 발생했을시 notification 알람 등을 보내는 형태로도 발전 가능
warning, error occur!
console.log
result is {
msg: 'error occur',
error: Error: some error
...some error trace
}