null書いたら負け。そんなあなたにオススメなのが、Optionです。
Optionを使うことで、nullの値を取る場合の処理が、いい感じに書けるのですが、如何せんhaxe.ds.Optionでは物足りなさを感じてしまいます。
そこで、HaxeのOptionをScalaのOptionっぽく使えるよう、拡張してみました。
ScalaのOptionのメソッドを全て実装するのは面倒なので、個人的に良く使うものを選んで実装しました。
ScalikeOptionクラスに、静的なメソッドを定義して、
あとはusingを使ってScalikeOptionのメソッドを呼ぶだけ、とても簡単。
import haxe.ds.Option;
using ScalikeOption;
class ScalikeOption {
public static function isEmpty<T>(opt:Option<T>) {
return switch(opt) {
case Some(v): false;
case None: true;
}
}
public static function isDefined<T>(opt:Option<T>) {
return !opt.isEmpty();
}
public static function getOrElse<T>(opt:Option<T>, defValue:T) {
return switch(opt) {
case Some(v): v;
case None: defValue;
}
}
public static function map<A, B>(opt:Option<A>, f:A -> B) {
return switch (opt) {
case Some(v): Some(f(v));
case None: opt;
}
}
public static function fold<A, B>(opt:Option<A>, defValue:B, f:A -> B) {
return opt.map(f).getOrElse(defValue);
}
public static function iter<A>(opt:Option<A>, f:A -> Void) {
switch(opt) {
case Some(v): f(v);
case None:
}
}
public static function flatten<T>(opt:Option<Option<T>>) {
return switch (opt) {
case Some(v): v;
case None: None;
}
}
public static function flatMap<A, B>(opt:Option<A>, f:A -> Option<B>) {
return switch (opt) {
case Some(v):
switch(f(v)) {
case Some(w): Some(w);
case None: None;
}
case None: None;
}
}
}
ScalaのOption.foreachは値を返さず副作用のみなのに対して、HaxeのLambda.foreachはBool値を返すので、ちょっと違います。Haxeで値を返さず副作用のみを取るのは、Lambda.iterなので、それにならい、ScalaのOption.foreachに対応するメソッドはScalikeOption.iterとしました。
ScalaのOption.getは、個人的に使っちゃいけないものだと思っているので、追加しませんでした。
使い方はこちら
import haxe.ds.Option;
using ScalikeOption;
class Sample {
static var s = Some(1);
static var n = None;
static var ss = Some(s);
static var sn = Some(n);
public static function main(){
trace("Some.getOrElse(0): " + s.getOrElse(0));
trace("None.getOrElse(0): " + n.getOrElse(0));
trace("Some.isEmpty: " + s.isEmpty());
trace("None.isEmpty: " + n.isEmpty());
trace("Some.isDefined: " + s.isDefined());
trace("None.isDefined: " + n.isDefined());
trace("Some.map: " + s.map(function(a) { return a * 10;}));
trace("None.map: " + n.map(function(a) { return a * 10;}));
trace("Some.fold: " + s.fold(99, function(a) { return a *10;}));
trace("None.fold: " + n.fold(99, function(a) { return a *10;}));
s.iter(function(a) { trace("Some.iter: " + a); });
n.iter(function(a) { trace("None.iter: " + a); });
trace("Some(Some).flatten: " + ss.flatten());
trace("Some(None).flatten: " + sn.flatten());
trace("Some.flatMap: " + s.flatMap(function(a) { if(a == 1) { return Some("one"); } else { return None;} }));
trace("None.flatMap: " + n.flatMap(function(a) { if(a == 1) { return Some("one"); } else { return None;} }));
}
}
ihxでこれを実行すると、
>> Sample.main();
Sample.hx:12: Some.getOrElse(0): 1
Sample.hx:13: None.getOrElse(0): 0
Sample.hx:14: Some.isEmpty: false
Sample.hx:15: None.isEmpty: true
Sample.hx:16: Some.isDefined: true
Sample.hx:17: None.isDefined: false
Sample.hx:18: Some.map: Some(10)
Sample.hx:19: None.map: None
Sample.hx:20: Some.fold: 10
Sample.hx:21: None.fold: 99
Sample.hx:22: Some.iter: 1
Sample.hx:24: Some(Some).flatten: Some(1)
Sample.hx:25: Some(None).flatten: None
Sample.hx:26: Some.flatMap: Some(one)
Sample.hx:27: None.flatMap: None
といった具合になります。
ちょっとしたものですが、nullのないコードが少しは書きやすくなると思います。
nullのないコードに幸あれ!