Kotlin で標準実装されているScope FunctionのうちのいくつかをC#(Unity) 向けに移植した話。
スコープファンクションでコードの見通しを改善する2018/09/26Gotanda.unity #8@yashims85
View Slide
誰● Twitter: @yashims85● 会社: モバイルファクトリー@五反田● Unity: 趣味でさわってる● 最近気になる言語: Rust● 緩募: 継続するダイエット
質問です皆さんはイケてないな。。。と思いながら、そんなもんだろうと諦めてコードを書いていることはありませんか?
● PBXProjectのプロパティ● 関数に外出しするほどではないちょっとした計算● Builder, Factoryパターンを採用するほどでもないちょっとした手順● 大量のnullチェックもしくは ?. 地獄私がイケてなぁと思いながらコードを書いているとき
スコープファンクションもしかしたらスコープファンクションがそれらの悩みを一助になるかもしれません。
スコープファンクション● Kotlinの文脈で登場する● (基本的に)引数にLambdaを1つとって、その中で自己参照をする● C#のExtensionで作った関数のように全部のObjectに生えてる
なるほど
いくつか移植してみたCSharpScopeExtensions:https://github.com/yashims/CSharpScopeExtensionsここでスター欲しいって言えば優しい人がしてくれるって聞きました
導入方法● releaseにunitypackageがおいてあるのでご自由にお使い下さい● もしくは簡単なのでコピペ
実際の実装今回は諸般の事情により2つだけ紹介
Also
Alsopublic static T Also(this T self, Action action){action(self);return self;}
Alsopublic static T Also(this T self, Action action){action(self);return self;}ジェネリクスのExtensionなので、どのObjectからも呼び出せる
Alsopublic static T Also(this T self, Action action){action(self);return self;}自分自身を引数に取る Actionに自分自身を渡す
Alsopublic static T Also(this T self, Action action){action(self);return self;}戻り値として自分を返す。
Let
Letpublic static R Let(this T self, Func action){return action(self);}
Letpublic static R Let(this T self, Func action){return action(self);}ジェネリクスのExtensionなので、どのObjectからも呼び出せる
Letpublic static R Let(this T self, Func action){return action(self);}自分自身をFuncに渡し、Funcの戻り値がLetの戻り値となる。
それがどしたの
実際の使用感をみてみる
Alsoの使い所例えばインスタンスのプロパティ設定
Alsoを使わないvoid Start(){Transform t1 = GetComponent();Quaternion q1 = new Quaternion();q1.x = 0;q1.y = 0;q1.x = 0;t1.rotation = q1;}
Alsoを使うvoid Start(){Transform t2 = GetComponent().Also((it) =>{it.rotation = new Quaternion().Also((that) =>{that.x = 0;that.y = 0;that.z = 0;});});}
Alsoを使う● Start()のスコープを一時変数で汚さない● いくつかの処理が記述されてるようならコレはメリット● 引数の変数名を`it`等の代名詞にしておけば、何に対しての処理かが明確になり、それ以外に言及するコードを避けられる● コレを使えばPBXProjectの設定が超絶スッキリする
Letの使い所例えばLINQのSelectの様に使う
Letを使わないTransform t = GetComponent();float distanceX = t.parent.transform.localPosition.x + t.localPosition.x;
Letを使うfloat distanceX = GetComponent().Let((it) =>{return it.parent.transform.localPosition.x + it.localPosition.x;});
Letを使う● LINQやRxのSelectと同じイメージで、Lambda内で変形を行える● Alsoと同じく、一時変数でスコープを汚さない
Letの使い所例えばC#6.xのnullableのunwrapping
Letを使わないstring say(string comment){return comment != null ? $"I said: {comment}" : "";}
Letを使うstring say(string comment){return comment?.Let(it => $"I said: {it}") ?? "";}
Letを使う● C#でのnull条件演算子「?.」は、nullチェックして、非nullであればローカル変数に格納するのと同義● それをLetの引数で受け取っているので、Letの中ではNull安全な変数を自由に使える
できなかったこと
できなかったこと● Lambdaの中でthisコンテキストを変える● KotlinではLambdaの中ではthisがそれ自身になるスコープファンクションがある(例: Apply)
Apply(実現できたら)public static T Apply(this T self, Action action){self.action();return self;}
Apply(実現できたら)public static T Apply(this T self, Action action){self.action();return self;}ジェネリクスのExtensionなので、どのObjectからも呼び出せる
Apply(実現できたら)public static T Apply(this T self, Action action){self.action();return self;}TをthisとしてActionを呼び出す
Apply(実現できたら)public static T Apply(this T self, Action action){self.action();return self;}自分自身を返す
Applyを使えたらvoid Start(){Transform t2 = GetComponent().Apply(() =>{this.rotation = new Quaternion().Apply(() =>{x = 0;y = 0;z = 0;});});}this以外の事について言及する余地が無くなる
まとめ
まとめ● ScopeFunctionを使うと一時変数が減らせ、コードの見通しが良くなる● ブロックで区切られることで、何について述べられているコードかが簡潔になる● Lambdaの中でthisを変える方法があったら誰か教えてください
ありがとうございました