いちいちnullチェックをしている時にふとscalaのOption型が恋しくなることがあります。
かと言って外部ライブラリ探してきて導入するのもなぁって思い軽めに実装した時のメモ。
早速コード
export interface Optional<A> {
isEmpty: boolean;
map<B>(f: (a: A) => B): Optional<B>
bind<B>(f: (a: A) => Optional<B>): Optional<B>
getOrElse<A>(a: A): A
get(): A
}
class Just<A> implements Optional<A> {
private value: A;
public isEmpty: boolean = false;
constructor(a: A) {
this.value = a
}
map<B>(f: (a: A) => B): Just<B> {
return Some(f(this.value));
}
bind<B>(f: (a: A) => Just<B>): Just<B> {
return f(this.value);
}
getOrElse(a: A): A{
return this.value;
}
get(): A{
return this.value;
}
}
class Nothing<A> implements Optional<A> {
private value: A;
public isEmpty: boolean = true;
constructor(a: A) {
this.value = a;
}
map<B>(f: (a: A) => B): Nothing<B> {
return None;
}
bind<B>(f: (a: A) => Just<B>): Nothing<B> {
return None;
}
getOrElse<A>(a: A): A{
return a;
}
get(): A {
throw Error("NoSuchElementException");
}
}
export const None = new Nothing(null);
export function Some<A>(a: A): Just<A>{
return new Just(a);
}
使用例
Some(10).map(a => a * 10).get()
=> 100
Option型ってmonad だよね?
モナド則とは以下
- (return x) >>= f == f x
- m >>= return == m
- (m >>= f) >>= g == m >>= (\x -> f x >>= g)
検証してみる
以下の様な関数fを定義しときます。
var f = a => {return new Just("FFF!!!")};
1. (return x) >>= f == f x
(Some("Monad!").bind(f).get() === f("Monad!").get()
2. m >>= return == m
new Just("Monad!").bind(Some).get() === Some("Monad!").get()
3. (m >>= f) >>= g == m >>= (\x -> f x >>= g)
関数gの定義
var g = (x) => new Just("GGG!")
var left = (new Just("Monad!").bind(f)).bind(g).get()
var right = new Just("Monad!").bind(x => {return f(x).bind(g)}).get()
left === right
まとめ
コードは短いですがライブラリ入れたほうが楽かもですね・・・