Scala implicit デザインパターン
Scala implicit デザインパターン
「implicit
。書いてあるコードは読めるけど自分で実装する時に使いどころがワカン。」 みたいのがあって職場の人に聞いたらいい感じのリンクを教えて頂いたので翻訳しつつ勉強がてらメモ。
目次
- 最初に
- Implicit Contexts
- Type-class Implicits
- Derived Implicits
- Type-driving Implicits
- まとめ
最初に
しばしば貧弱Scalaエンジニア(俺)達から畏敬の念とともに語られるimplicit
。実はそれ自体の機能はそんなに強力じゃないみたい。
implicit parameter
: 明示的に引数のを渡す必要なく、その型とスコープ内の値に基づいて自動的に推論implicit conversion function
: 要求に応じて明示的に関数を呼び出す。
ただ単純に使用するのではなく、長い歴史と数多くのエンジニアたちの知恵を礎に最適な使用方法(=デザインパターン)が生まれた。4つほど紹介してみる。
Implicit Contexts
DB Connectionとか 何回も何回も使用する引数を、渡す度に定義してたらめんどくさいよね。値があるなら空気読んでそれ使ってよ。 という話。
ex) 非同期処理する全てのメソッドにExecutionContextを引き渡す
// 全てのメソッドが`ExecutionContext`を必要としている def getEmployee(id: Int)(implicit e: ExecutionContext): Future[Employee] = ??? def getRole(employee :Employee)(implicit e: ExecutionContext): Future[Role] = ???
getEmployee()
, getRole()
の両方でカリー化された引数でExecutionContext
を要求している。
// implicitではない雑魚 val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global val bigEmployee: Future[EmployeeWithRole] = getEmployee(100)(ec).flatMap { e => getRole(e)(ec).map { r => EmployeeWithRole(e.id, e.name,r) }(ec) }(ec)
implicitを使わない場合全ての呼び出し箇所で明示的に(ec)
を引き渡す必要がある。すごくめんどくさい。 4つならまだいいけどもっと数が増えてきたり、複数箇所で使用されていたらもうめんどくさすぎる。
// implicit parameterを使うぞ! implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global val bigEmployee: Future[EmployeeWithRole] = getEmployee(100).flatMap { e => getRole(e).map { r => EmployeeWithRole(e.id, e.name,r) } }
implicit
キーワードとともにExecutionContext
を定義することで、全ての使用箇所で明示的な引数の引き渡し(ec)
を全て削除できる。やったねたえちゃん!という話。
Implicit Contextsの定義
- 通常ジェネリックス型ではなく、型パラメーターも持たない。
- 異なるシグネチャを持つあらゆる関数に同じ
implicit
が渡される。 implicit
は呼び出すタイミングによってその都度値が評価される。- play frameworkではHTTPリクエストの度に
Request
オブジェクトの値が変わる
- play frameworkではHTTPリクエストの度に
- AkkaのActerSystemのような場合には
implicit
の値は変更可能な場合がある。(これは意味不明誰か教えてくれ)
Implicit Contexts
パターンは "ボイラープレートな引数を渡す便利な方法" であり、implicit parameter
の唯一使用方法である。それ以外は一般的な引数と違いはない。
Type-class Implicits
いろんな型からJsonに変換したい場合とかにパターンマッチやオーバーロードで実装もできるけど、全ての型を記述するのは不可能でなによりコンパイル時にわからないし、同名関数を定義しまくるのは汎用性がない。変換可能な型が事前にわかればコンパイルエラーで防げるし、呼ばれるべき関数が型でわかれば汎用性もあるよねという話。
// Jsonオブジェクトの定義 sealed trait Json object Json{ case class Str(s: String) extends Json case class Num(value: Double) extends Json // ... もっとたくさん } def convertToJson(x) // このメソッドで x をJsonオブジェクトに変換したい
このような場合 def convertToJson(x): Json
の実装はどのようなパターンが考えられるだろうか。
パターンマッチ
まずは前述のパターンマッチで実装してみる。
// パターンマッチで convertToJson を実装してみる def convertToJson(x: Any): Json = { x match{ case s: String => Json.Str(s) case d: Double => Json.Num(d) case i: Int => Json.Num(i.toDouble) // float, short などさらに多くのケースが考えられる } }
さあ。このconvertToJson()
はどうだろうか...?勿論パターンマッチで実装済みの型からの変換は正しく動くだろう。しかし、未実装の型(Boolean
やjava.io.File
)の場合はどうだろ? 実行時エラーで失敗してしまう。 現実的に考えて全ての型のパターンマッチを実装するのは不可能であるし、何より変換できない型が渡された場合はコンパイラーに止めてほしい。この問題を解決するのがType-class Implicits
パターンだ。
オーバーロード
オーバーロードを使用することで複数パターンのconvertToJson()
を実装することができるかもしれない。
def convertToJson(t: String) = Json.Str(t) def convertToJson(t: Double) = Json.Num(t) def convertToJson(t: Int) = Json.Num(t.toDouble)
このように同名で引数の型が異なる関数を複数用意しておけば、前述の未実装の場合にコンパイルエラーが発生しない問題は回避できる。
しかし、convertToJson()
のような関数を複数作成しようとした場合どうなるだろうか?
def convertToJson(t: String) = Json.Str(t) def convertToJson(t: Double) = Json.Num(t) def convertToJson(t: Int) = Json.Num(t.toDouble) def convertToJsonAndPrint(t: String) = println(convertToJson(t)) def convertToJsonAndPrint(t: Double) = println(convertToJson(t)) def convertToJsonAndPrint(t: Int) = println(convertToJson(t)) def convertMultipleItemsToJson(t: Array[String]) = t.map(convertToJson) def convertMultipleItemsToJson(t: Array[Double]) = t.map(convertToJson) def convertMultipleItemsToJson(t: Array[Int]) = t.map(convertToJson)
こうなる。
オーバーロードは同名関数という規約があるので、少し役割を変えた関数を定義しようとした場合。その新しい関数でも全ての型を網羅するオーバーライドを記述する必要がある。これはもうさいあくである。
Type-class Implicits パターン
- パターンマッチは全パターンを網羅することができない
- オーバーロードは汎用性がない
ここでType-class Implicits
パターンを使用してみよう!
trait Jsonable[T]{ def serialize(t: T): Json } object Jsonable{ implicit object StringJsonable extends Jsonable[String]{ def serialize(t: String) = Json.Str(t) } implicit object DoubleJsonable extends Jsonable[Double]{ def serialize(t: Double) = Json.Num(t) } implicit object IntJsonable extends Jsonable[Int]{ def serialize(t: Int) = Json.Num(t.toDouble) } }
こん感じで書いておけば...
def convertToJson[T](x: T)(implicit converter: Jsonable[T]): Json = {
converter.serialize(x)
}
このようにconvertToJson()
を実装できる!!!!
convertToJson()
のカリー化された引数にimplicit
でJsonable[T]
型を受け取っている。この[T]
はconvertToJson[T]
で束縛されている。
例えばx
がString
であった場合、このような挙動になる(っぽい)。
x
がString
なのでJsonable[T]
もJsonable[String]
に束縛されるJsonable[String]
はimplicit
なのでscalaが使えるimplicitがないか捜索の度に出る- まずスコープ内を探す、この場合は定義していないので見つからない
- 次は
Jsonable
のコンパニオンオブジェクトを探しに行く、今回はStringJsonable
を定義済みなのでここで見つける - scala「お!定義済みだね!通れ!」となる
もし、Jsonable[File]
のような未定義のクラスの場合。scala「だめだ!お前は!implicitが見つからないぞ!コンパイルエラーだ」となる。
さらに、前述の def convertToJsonAndPrint(t: String) = println(convertToJson(t))
のような関数を定義した場合も「お!StringからJsonに変換するimplicitがほしいんだね!StringJsonableを使いな!」となる。やったね!
Type-class Implicitsと型クラスimplicitの違い
Type-class Implicits
は評価する度に使用するimplicitを探しにいく、ライブラリで実装済のものを使う場合もあれば、自前で実装した物を使用する場合もある。Type-class Implicits
は通常、データを保有しており、しばしば変更可能型クラスimplicitは純粋な関数である場合が多い。(まったく理解していない)
Derived Implicits
Type-class Implicits
では深い階層の呼び出しも型安全に行えるよ!イエ〜イ! という話。
前述のJsonクラスでStr
やNum
の他にJson List
も実装したいとしたらどうすれば良いだろうか?
sealed trait Json object Json { case class Str(s: String) extends Json case class Num(value: Double) extends Json // ... and more }
Str
とNum
は実装済みであるのでconvertToJson()
は成功させたい。しかし、File
型などの未実装の場合はconvertToJson()
でコンパイルエラーにして防ぎたい。
// これは実行できる convertToJson(Seq(1, 2, 3)) convertToJson(Seq("baz", "bar", "foo"))
// これはコンパイルエラーにしたい convertToJson(Seq(new java.io.File("."), new java.io.File("/")))
どうすればこの問題を解決できるだろうか?
答えは先ほど追加したJsonable
にSeqをserializeするimplicit SeqJsonable
を作成。このSeqJsonable
内のSeqを[T]
型に束縛してしまえばいいのだ。
きっと日本語でおkなのでコードを見てみよう!
trait Jsonable[T]{ def serialize(t: T): Json } object Jsonable{ implicit object StringJsonable extends Jsonable[String]{ def serialize(t: String) = Json.Str(t) } implicit object DoubleJsonable extends Jsonable[Double]{ def serialize(t: Double) = Json.Num(t) } implicit object IntJsonable extends Jsonable[Int]{ def serialize(t: Int) = Json.Num(t.toDouble) } // このSeqJsonableを新しく追加 implicit def SeqJsonable[T: Jsonable]: Jsonable[Seq[T]] = new Jsonable[Seq[T]]{ def serialize(t: Seq[T]) = { Json.List(t.map(implicitly[Jsonable[T]].serialize):_*) } } }
このdef serialize(t: Seq[T])
の[T]
型の束縛のおかげで、implicit
実装済みの型は呼び出せますが、未実装のjava.io.File
型などは呼び出し時にコンパイルエラーになります!めでたし、めでたし。
実はDerived Implicits
パターンはこれだけではありません。どれだけネストした型でも型安全に呼び出すことができるのです!
これも日本語でおkなのでコードを見ていきましょう!
// コンパイル可能 convertToJson(Seq(Seq(Seq(1, 2, 3)))) // コンパイルエラー convertToJson(Seq(Seq(Seq(new java.io.File(".")))))
どれだけネストが深くても関係ありません。コンパイラが再帰的に型を判定し[T]
型にマッチしなければコンパイルエラーに変換します。
この制約は一見面倒に感じるかもしれません。仮にJsonable
が外部ライブラリだった場合、前述のようにjava.io.File
型はコンパイルエラーなってしまうため実行できないからです。しかし、これはメリットでもあります。
存在しなければjava.io.File
型のFileJsonable
を実装すれば、Seq[File]
も同様に扱うことができるのです。
// これを作ってあげる implicit object FileJsonable extends Jsonable[java.io.File]{ def serialize(t: java.io.File) = Json.Str(t.getAbsolutePath) } // これも実行可能になる convertToJson(Seq(Seq(Seq(new java.io.File(".")))))
このように型の束縛は実装を制限するのではなく、ユーザー側に拡張性という大きなメリットをもたらします。
Type-driving Implicits
より大きな型に変換しようとすると予想外の動きをすることがあるけど、一つの関数が引数の型に応じて戻りの型を自動で変換してくれた楽チンだよね! という話
ex) Byte -> Short -> Longに変換
// Byteで定義 val x: Byte = 123 x: Byte = 123 // Byte -> Shortに変換 val y: Short = x y: Short = 123 // Short -> Longに変換 val z: Long = y z: Long = 123L
これは結構便利。しかし、予想外の動きをする場合があります。
// Longの最大値 val bigLong = Long.MaxValue - 1 bigLong: Long = 9223372036854775806L // Long -> Floatに変換 val bigFloat: Float = bigLong bigFloat: Float = 9.223372E18F // Float -> Longに戻す val bigLong2: Long = bigFloat.toLong bigLong2: Long = 9223372036854775807L // 最初のLongと一度Floatを通したLongを比較 bigLong == bigLong2 > res40: Boolean = false
false!!!!!!!
このような事態を防ぐにはどうすれば良いでしょう?型の拡張を自動で行う関数を定義できます!
{ def widen[T, V](x: T)(implicit widener: Widener[T, V]): V = widener.widen(x) class Widener[T, V](val widen: T => V) object Widener{ implicit object FloatWiden extends Widener[Float, Double](_.toDouble) implicit object ByteWiden extends Widener[Byte, Short](_.toShort) implicit object ShortWiden extends Widener[Short, Int](_.toInt) implicit object IntWiden extends Widener[Int, Long](_.toLong) } }
こんな感じで、与えられた型を自動で拡張するwiden()
関数を作成します!さぁ呼び出してみましょう!
// Floatを渡すとDoubleが返ってくる widen(1.23f: Float) > res12: Double = 1.2300000190734863 val smallValue: Byte = 123 // Byteを渡すとShortが返ってくる val shortValue = widen(smallValue) > shortValue: Short = 123 // Shortを渡すとIntが返ってくる val intValue = widen(shortValue) > intValue: Int = 123 // Intを渡すとLongが返ってくる val longValue = widen(intValue) > longValue: Long = 123L
戻りの型は最初のようにscalaのコンパイラが自動で判断するのではなく、Widener
に定義されているimplicit
の実装で決定されます。それだけではなく、widen(longValue: Long)
このように未実装な型を渡した場合はコンパイルエラーで防ぐことができます。
このDerived Implicits
は、一般的にTuple
を大きなTuple
に拡張する時にも用いられます。
{ def extend[T, V, R](tuple: T, value: V)(implicit extender: Extender[T, V, R]): R = { extender.extend(tuple, value) } } case class Extender[T, V, R](val extend: (T, V) => R) object Extender { implicit def tuple2[T1, T2, V, R] = Extender[(T1, T2), V, (T1, T2, V)] { case ((t1, t2), v) => (t1, t2, v) } implicit def tuple3[T1, T2, T3, V, R] = Extender[(T1, T2, T3), V, (T1, T2, T3, V)]{ case ((t1, t2, t3), v) => (t1, t2, t3, v) } implicit def tuple4[T1, T2, T3, T4, V, R] = Extender[(T1, T2, T3, T4), V, (T1, T2, T3, T4, V)]{ case ((t1, t2, t3, t4), v) => (t1, t2, t3, t4, v) } // ... tuple21 まで続くよ ... }
widen()
の時と同じで、戻り値の型はExtender
で定義したimplicit
に依存します。結果、Tuple2
とTuple3
はクラス階層では互いに直接関係がありませんので、Tuple2
でextend()
を使用することができ、scalaコンパイラは結果をTuple3
であると自動的に判断します。
// Tuple2を宣言 val t = (1, "lol") t: (Int, String) = (1, "lol") // Tuple3に拡張 val bigger = extend(t, true) bigger: (Int, String, Boolean) = (1, "lol", true) // Tuple4に拡張 al evenBigger = extend(bigger, List()) evenBigger: (Int, String, Boolean, List[Nothing]) = (1, "lol", true, List())
Type-driving Implicits
パターンを使用することで、implicit
パラメータのどのインスタンスが有効範囲にあるかに応じて、関数の戻り値の型をscalaコンパイラが推測する方法を制御できる。これは少しクレーバーなテクニックです。戻りの方を制御するのは気づかない可能性もありますし何でもかんでもType-driving Implicits
パターンを使用すれば良いというわけではありません。しかし、効果的に使用すれば非常に有効なパターンになります。
ex) fastparse ではこのExtender
に似た形で実装されています。
最後に
これでimplicit
を使用したいくつかのデザインパターンの紹介は終わりになります。しかし、これで全てを説明しきったわけではありません。ここで説明しなかった使用方法を最後に少し言及します。
- Shapeless の Aux Pattern。
Shapeless
ではimplicit
を使用してできるほぼ全てのことが網羅されています。しかし、ここで全てに言及するのは不可能なので興味があれば除いて見てみてください。 implicit parameters
の代わりにimplicit conversions
を使用するもの。 ex)implicit constructors
やその拡張クラスを実装するもの。(C#のものに似ている)- 前述のCollectionのようなものだけではなく、
case class
で動作するDerived Implicits
パターンも存在します。Shapelessでも実装されています。 Implicit Contexts
はよりパワフルに、行番号やファイル名などコンパイラからさらなる情報を取得することもできます。ClassTag や sourcecodeで実装されています。
以上です!!マサカリ待っています!
参考
http://www.lihaoyi.com/post/ImplicitDesignPatternsinScala.html
Rakus Meetup Tokyo #1 終わらないスクラム 行って来た
こちら行って来た
テックブログもあるよとのこと
アジェンダ
- 終わらないスクラム ~楽楽精算のサービス拡大を支えるスクラム開発の取り組み
- テックリード(自称)としてのやっていき! ~iOSアプリ開発チームを率いて
- 流行の開発手法ChatOpsとは ~楽楽明細チーム/ChatOpsでルーティン作業をまるごと自動化~
終わらないスクラム
概要
ラクスでは、まだ多くの開発チームがウオーターフォール型の開発プロセスを採用していますが、 一部のチームでスクラムによるアジャイル開発に取り組んでいます。 今回は楽楽精算チームでの取り組みを紹介します。 実際にやってみると様々な問題が発生しました。問題解決に向けたアクションや取り組みを通じて得た知見、今後の課題等を事例を交えてお話しします。
登壇者
資料
背景
フォーターフォールからスクラムへ
Problem
- バーンダウンしない
- めっちゃわかる
- とにかく会議隊が終わらない
Try
- タスクをなるべく細分化
- リファインメントの徹底
- デイリースクラムの見直し
- スクラムマスターが朝会の司会をしない
- カンバン
- 物理カンバン
- キャパシティーを表す写真を貼って、4枚以上はキャパオーバーとか測る
- インペディメントの除去
知見
- 与えられた役割を全うする
- スクラムチームの役割
- プロダクトオーナー
- 開発チーム
- スクラムマスター
- 役割を全うするのはどのような開発手法でも同じ
- 短期的なスプリントを何度も繰り返すので従来の開発より振り返り・導入の機会が多い
これからのスプリントでの挑戦
感想
- スクラムに向かって行ってる感じがしていいなって思った
- チーム多いなって思った(10人)
- 1年後くらいに振り返ったら全然違うチームになってそう
- 物理カンバンのだんだん汚くなるのと付箋の粘着力問題と立ち向かっててすごいなって思った
- バーンダウンしないのめっちゃわかる。
- 見積もりにくそ時間かけないと正しく測れない、正しく測れないから後からタスクが湧いて来てバーンダウンがなんなら右肩上がりになる
- 正しく見積もろうとするとそれだけで数日経つのでコードが書けない。みたいなトレードオフになる気がする
テックリード(自称)としてのやっていき!
概要
若手エースエンジニアが初めてのiOSアプリ開発でテックリードとして奮闘したお話しです。 iOSアプリ開発は、自身も初、メンバーも初、しかも短納期(3ヵ月...)。 このデスマーチを予感させる不利な条件下で、テックリードとしてどのようにチームをリーディングしたのか。 様々な事例を交えてご紹介します。
登壇者
背景
スクラムチーム
やっていった
- 仕組み化
- コード品質
- バランスをとる
仕組み化
- DDD
- 詳しい人がいないのでそこまでがんばんない
コード品質
- あえて属人化させて開発速度をあげる
- スタイルガイドなど導入
- CIとか外部のやつ
- なんか聞いたことないやつだった
- コードレビュー
- 細かいところまでは見れないので要点を絞ってレビュー
バランスをとる
- hubになる
- このへん仕事のSlackきてあんま聞けなかッタ...
振り返り
- 技術的な課題感
- 未着手の言語だとさすがに大変そう
- エンジニアリング頑張りたい
感想
- チャレンジする感じいいなって思った
- 「3ヶ月くらいで一気に作ったけどtoilで死んだ」みたいな話をいつか聞きたい
- php/java -> swiftってどうなんだろ
- 大変そう(小並感)
- 挑戦して成果出してアウトプットまで繋げるの偉いなって思った(こなみ)
流行の開発手法ChatOpsとは
概要
Jenkinsの導入で自動化が進んだけど、「Jenkinsを毎回開くのは面倒」、「アカウントの作成も面倒」、「非エンジニアに使ってもらうにはちょっとハードルが高い」。 そこで導入したのがChatOps! Slack互換のチャットツール「Mattermost」でルーティン作業を丸ごと自動化しました。 利用したbotツール、システム構成、Hubotスクリプトの実例など、ノウハウを余すことなくご紹介します。
登壇者
資料
背景
- 少人数で開発~リリースまでを支える自動化の紹介
環境
- 開発者がすきなScript書いたり
- やばそう
- CI/CDはJenkinsに
問題点
- Jenkinsだるい
- だよね
ChatOps
- IncomingWebHooks
- SlackCommands
- OutgoingWebhooks
hubotを使う
- Slackコマンドのリクエストを受け取るAppサーバー立てるの怠い
- coffeeじゃなくていい
- npmのパッケージも作って公開した
- すごい
- 活用事例は若干コンプラっぽそうだったので割愛
感想
- サービス外のコーディングは楽しい
- わかる
- hubotどこで動いてるんだろ
- digdag入れたい
- 今はhubto以外も結構あるぽい
- Q.意識高い人が作ったものってその人がいなくなるとわからんちん問題はどうしてるのだろう
- A.まだ引き継いだりとかっていう段階じゃない。でも最高のコードを書いてるから大丈夫!
最後に
- 次回は来年(2020年)2月ごろだそうです。
Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました
株式会社サイバーエージェント AdTechStudio オペレーションテクノロジー事業部 iXamDrive(イグザムドライブ) でサーバーサイド・フロントエンドの開発をしている @HonMarkHunt です。
先日、 @konifar さんの記事 『B1グランプリ(Bug Bash)』を2時間やって細かいIssueを洗い出しました
を拝見し素晴らしいこころみだと思ったのでiXamDriveでも挑戦して見ました!
* iXam Driveとは?
インフィード広告の運用自動化ツール
TL;DR
- チーム内で触ってバグを見つけるのでは無く他チームのエンジニアにプロダクトを触ってもらいバグや分かりづらい点(UI指摘)を見つけてもらった。
- 1時間でバグ・UI改善点が27個見つかった
Bug Bash!!
このような流れで行いました!
- 事前説明
- バグ報告
- 採点
- 景品授与
それぞれ解説していきます!
事前説明
まず、当日のレギュレーションを発表しました。大きく分けるとレギュレーションは以下の3つです!
フロント(画面)
、サーバーサイドのコード(GitHubリポジトリ)
、フロントエンドのコード(GitHubリポジトリ)
から自由にbag(と思わしきもの)
、UI/UX的な指摘
、その他なんでもの
を見つけて欲しい。フロント(画面)
はStaging環境を使う。- 本番環境だと実データに影響を及ぼす危険操作ができてしまうため
フロント(画面)
は全機能を自由に触わるのではなく、こちらで指定した機能のみ触ってもらう。- 一部昨日はStaging環境でも実データに影響を及ぼす危険操作ができてしまうため
どの機能を触って貰うかミーティングで 危険操作だらけだった ことが判明したのも収穫でした。
バグ報告
- バグと思わしきものを見つけた方は前に出てプロジェクターに繋いでバグ内容を発表してもらいました。
- 想定の10倍くらいバグだらけだったのでバグ報告の行列もできてしまいました。
採点
- 報告されたバグはその都度iXamDriveのメンバーで採点しました。
- 4人のジャッジがそれぞれ1~5点までの札を持っていて、ジャッジの合計点数でもらえる景品が変わるという評価基準にしました。
- 0点の場合は「仕様」という札をあげる。
(札は前日にiXamDriveメンバーで割り箸と段ボールで手作り)
景品授与
- 報告のたびに採点してその都度点数に応じた景品を持って行って貰いました!
- 景品用に大量の駄菓子と
机にあったいらない本技術書を用意しました! - ちなみに フォームにHTMLタグをそのまま入力できる という痛恨の初歩的バグ を発見していただいた方が満点20点を叩き出しシークレット商品の iXamDriveの飲み会1回無料参加券 を獲得しました。
景品交換の様子
終了後
- Bugは随時記録。
- 27個ものバグが出ました!!!
振り返り
Bug Bashをやってみて良かったこと改善点などを書いていきます。
良かったこと
- @konifar さんの記事を拝見してから、チームのKPT meetingでBugBash 1週間後の開催が決まりました。開催までの期間が短く、2度のmeetingを経て、他チームの方々への周知用チラシ作成、採点用札作成、景品準備など... ドタバタしながら進めていくのが文化祭みたいで楽しかったです。心なしかチーム力も向上した気がします!!
- 参加していただいた方々にも楽しんでいただけた。参加者にはアンケートをお願いしたのですが結果がとてもよかったです!!「他チームでも開催したい。」という声があったのは嬉しかったです!
- issueがいっぱいできた。 ユーザーテストや単体テストと違い、自身のプロダクトを他チームのエンジニアに触ってもらう機会は中々ないと思います。エンジニアは景品欲しさに能動的攻撃を仕掛けてくるので普段は気づかないバグを発見することができました。
改善点
- できたissueいつ消化する?問題。 issueはたくさんできたものの開発スケジュールにそんなに空きがあるはずもなく... とりあえず取り組めそうなものはバックログに追加しておいたので、一瞬手が空いた時にサッと着手できるといいかもしれないです。BugBashででたバグは当月中に解消するなどのルールを事前に決めておいてもよかったかもしれないです。
- Bugの捜索対象を
フロント(画面)
、サーバーサイドのコード(GitHubリポジトリ)
、フロントエンドのコード(GitHubリポジトリ)
と3つ選出しましたが、ほとんどがフロント(画面)
の指摘でした。アンケートでも「コードの指摘はそもそも仕様がわからないからハードルが高かった」
とありました。機能を絞ったりしてコードも見やすいように手配すれば良かったです。
Tips
- 発言しやすい空気を作るように心がけました。どんな報告が来てもとりあえず大声を出しました。
- 得点のフダ余ってるんで使いたい人がいたら言ってください。
- お祭り感を出すためにわざわざチラシを作って他チームに配りました。
Shinjuku.LT 第18回 レポ
ShinjukuLT
目次
- DI with Reader Monad
- Search Suiteのシステム構成
- 引っ越しを支えるかもしれない技術
- ツリー構造のデータをRDBで扱う
- 第一回 デザインクイズ
- Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました
- カイゼンジャーニー読んだよ
DI with Reader Monad
登壇者
TL;DR
- 関数単位の細かなDIができる
- 評価されるまで、DIを遅延させることができる
DI
- 外部から依存を注入できるようにするデザインパターン
メリット
-
- テストしやすい
種類
- 動的DI
- 静的DI
SwiftでDI
- Swinject
- DIKit
Reader Monad
- コードがたくさんです!
感想
- 心の片隅に覚えておこう。
Search Suiteのシステム構成
* 全面コンプラ
引っ越しを支えるかもしれない技術
登壇者
TL;DL
- 引っ越しで支えるかもしれないいろんな情報
引っ越しにかかるコスト
発送買取によるメリデメ
メリット * 重いものを運ばなくていい
デメリット * 引っ越しで住所が変わるとだるい
家電や粗大ゴミの破棄
- 家電は捨てれない -> 高い
- 粗大ゴミは低コストで捨てれるが、予約が必要
- 地元の引っ越し屋さんの有料回収した
- ネットのリサイクルショップは反応が遅くて微妙
- ミニクラ 等で預かってもらっても良かった
物件探し
- レインズ見れない(見たい)
感想
- レインズ見たい
ツリー構造のデータをRDBで扱う
登壇者
ツリー構造のデータで考慮すること
- 全体参照
- 一部参照
- 挿入
- 更新
- 削除
ツリー構造をRDBでどう表現するか
- 隣接リスト
問題点
- 階層が深くなると一度に引くのが大変になる
- 分けてクエリを発行してもクエリの発行回数が増える
- 挿入・更新は容易だが、削除は(末端以外だと)大変
- 隣接リストを用いたツリー構造は
ナイーブツリー
と呼ばれるアンチパターン
解決策
代わりのツリーモデルを使用する
- ただしやや複雑になるので注意
1 . 経路列挙
経路列挙
- 参考 : http://blog.amedama.jp/entry/2016/05/06/035437
- 子供のリストへの参照は簡単
- 削除・更新は容易だが参照整合性を維持することができない
閉包テーブル
- メモが追いつかなかった
まとめ
- ツリー構造の隣接リストはアンチパターンになることがある
- 経路列挙は参照整合性が維持できない
- 万能っぽいが、構造の難しさとデータが増える
感想
- 全然知らなかった
第一回 デザインクイズ
登壇者
デザインクイズ
- 作者: 田中クミコ,ハラヒロシ,ハヤシアキコ,ヤマダジュンヤ
- 出版社/メーカー: 翔泳社
- 発売日: 2013/08/20
- メディア: 大型本
- この商品を含むブログを見る
こちらの本から抜粋してAとBのどちらのデザインが直感的に優れているのかのクイズ大会
大事なこと
- 誰のためのデザインなのか
- 何のためのデザインなのか
感想
- 楽しかった!
Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました
登壇者
- 僕です
資料
カイゼンジャーニー読んだよ
登壇者
カイゼン・ジャーニー たった1人からはじめて、「越境」するチームをつくるまで
- 作者: 市谷聡啓,新井剛
- 出版社/メーカー: 翔泳社
- 発売日: 2018/02/15
- メディア: Kindle版
- この商品を含むブログを見る
- 小説 + 解説という面白い構成
- リアルな状況に即したベストプラクティスに即している
感想
- 「あなたは何をしている人なんですか?」
Shinjuku.LT 第17回 レポ
ShinjukuLT
目次
- Shinjuku.fitness
- SQL Format1
- ブランチとCLRを組み合わせて、論理の繋がりを見える化してみる
- Lensとは?
- NGINX Blogから考えるマイクロサービスのProxy設定
- 危機的状況から脱出した話
Shinjuku.fitness
https://t.co/05KWn2ZQbl#17 最初の発表は@HonMarkHunt @futesi_nebusoku の「https://t.co/3FP9zpHbxT」です!#shinjukult pic.twitter.com/HQ1gIvhwzD
— disc99 (@disc99_) 2018年2月18日
登壇者
TL;DR
- 正しい肉体改造方法
資料
感想
- 1時間40分も喋ってしまった。LTとは?
SQL Format1
登壇者
TL;DR
- SQLフォーマッター作ってみた
様々なSQL文のフォーマット
- フォーマットが人それぞれ
select a , b , c from hoge
select a, b, c from hoge
select a,b, c from hoge
- 公式フォーマットは存在しない
- サイモンホリウェルのフォーマットなど
合意をとる
- 意外と色々めんどくさい...
自動でフォーマットしてくれたら楽!!
- 作ってみた
1. ファイルパスを引数に指定された`.go`ファイルを書き換える 2. `sql`という変数に格納されている文字列を修正する
- 手順
1. astで抽象構文木を抽出 2. 変数取得 3. パーサーで解析、フォーマット 4. 抽象構文木に入れる
デモ
- ターミナルで見せてくれた
感想
- ファイル保存時にフォーマット直接書き換えて欲しいナ
ブランチとCLRを組み合わせて、論理の繋がりを見える化してみる
登壇者
TL;DR
- CLRの順番に質問してブランチで図に起こすと発言の意図をぶれずに伝えることができる
宣伝
- 電車のお悩みを解決するオンラインハッカソンに挑戦して豪華賞品をゲットしよう!
前置き
ブランチ
https://www.slideshare.net/NoriyukiMizuno/vol1-66046068/30
- 原因と結果をはっきりさせる
- AだからB
- AでBだからC
CLR
- 論理性を検証する4つの視点
- 4つの視点を正しい順番で質問する
1. 明瞭性の懸念 2. 存在の懸念 3. 因果関係の懸念 4. 十分性の懸念
- 詳しくはブログにて
資料
hiiiiiiihikaru.hatenadiary.com
感想
- 身につけたら一生モノのスキルになりそう
Lens
登壇者
TL;DR
- Lensとは何か?
宣伝
Lensとは?
- なんかすごいGetter/Setter
普通のGetter/Setterとなにが違うの?
- ネストしたオブジェクトのある一部の値だけ操作したい
関連Link
資料
感想
- composeの実装、全然わからなかった
NGINX Blogから考えるマイクロサービスのProxy設定
登壇者
TL;DR
- マイクロサービス化 => プロキシのやること増えた => アプリ/ミドルウェアで解決するの辛い =>
よくある構成
リバースプロキシ
- セキュリティ
- SSL
- ロードバランシング
- キャッシュ
- etc...
マイクロサービスのプロキシはどうあるべき?
従来のアプローチ
- ミドルウェアやFWで解決
- 覚えたり運用するの大変...
3 Models in the NGINX
1. Proxy Model 2. Router Mesh Model 3. Fabric Model
Proxy Model
- 従来のリバースプロキシの設定 + マイクロサービス特有の機能
Router Mesh Model
- 1台目のNIGNXで従来のリバースプロキシのような動き
- 2台目のNGINX(ルーターメッシュハブ)
- エンドポイントの統合
- 内部キャッシュ
- サーキットブレイカー
- etc...
Fabric Model
- マイクロサービスコンテナごとにプロキシを配置
- 全ての通信IOがプロキシ経由
で、具体的にどうすればいいの?
SidecarとService Mesh
Sidecar
- 各サービスとセットでデプロイ
- 全ての通信がプロキシを経由
Service Mesh
- ≠Fabric Model
新しめのツールがたくさんあるぞ!
資料
感想
- Proxy ModelからService Meshへ移行するぞ!!
危機的状況から脱出した話
登壇者
TL;DR
- 全面コンプラ
Shinjuku.LT 第16回 レポ
ShinjukuLT
目次
- キャラクターマーケテティングを考えてみる
- Physical Computing2 現実(リアル)を支配ハックする
- Elastic Cloudを1年ばかし使ってみたレビュー的な
- 入社までにやっておきたい事, 入社してすぐにしたい事
- ~歴史から学ぶ~ スニーカー基礎講座Ⅰ
キャラクターマーケテティングを考えてみる
登壇者
TL;DR
動機
キャラクターを作るということ
- 差別化や認知度UP
- たくさんのメリット
- 定着させていくためには...
- 一定の予算と時間が必要
- PR
- 作り込み
- が必要
成功例
- 新幹線開通にあわせて登場
- 熊本外の人に向けてSNSなどで拡散
- ターゲットと時期が明確
人気キャラを作るため
- 世界観
- 背景
- 差別化
- 使いやすい
実際に作ったキャラクター紹介
- コンプラのためお見せできない
- とても可愛かった
所感
デザインできるのっていいナー
Physical Computing2 現実(リアル)を支配ハックする
登壇者
TL;DR
- ブラーバを作る
方向転換する
- モータードライバ
- 前進と後進ができるようになった
壁に近づいたら後進する
所感
自分で掃除した方が早そう
Elastic Cloudを1年ばかし使ってみたレビュー的な
登壇者
宣伝
TL;DR
経緯
- 障害対応、顧客サポートでELBログを可視化したい
- ElastiSearchとKibanaよさそう
- ElastiSearchは管理が大変...
- Amazon ESとElastic Cloudとの比較
状況
- 要求: 管理に時間がかからないこと
- ほぼAWSの構成
比較
Amazon ESのメリット
Elastic Cloudのメリット
- ESと同社が作成しているので対応がきめ細やか
- X-Pack標準搭載
- クラスタのバージョンアップが容易 違いはそんなになかったがバージョンアップやESの使い勝手からElastic Cloudを採用
結果
- 安定している
- version up対応もスムーズ
- 不満な点は特になし
所感
- うちはlogentriesです
入社までにやっておきたい事, 入社してすぐにしたい事
TL;DR
- 入社初日から活躍するためにやっておきたいこと
準備しておきたいこと
- ビジョン・行動指針の把握
- 技術面のキャッチアップ
- 公開されている記事や動画には目を通す
- 使用しているライブラリ使ってみる
- ビジネス視点でサービスさわってみる
- 中の人とご飯に行く
- 事前に親睦を深めておく
- お引越し
- オフィス側に引っ越せるなら是非
- ビットコイン買っておく
- みんなやってるから
最初は...
- 最初の一ヶ月は「なんでも聞いていい魔法の時間」
入ったあとやりたいこと
- ひたすらストーキング
- Slackの過去ログ
- ドキュメント, PR
- 闇雲でなく脳内にインデックスをつくっていく感覚
- 内容じゃなくて背景を知る
HowWhy- なぜこうなっているのかを意識する
- ビジネスドメインの理解
まとめ
- ストーキングしよう
- Whyを理解しよう
- ドメインを理解しよう
~歴史から学ぶ~ スニーカー基礎講座Ⅰ
登壇者
僕です
スライド
【読書メモ】Scala関数型デザイン&プログラミング 1章 関数型プログラムの基礎
Scala関数型デザイン&プログラミング ―Scalazコントリビューターによる関数型徹底ガイド (impress top gear)
- 作者: Paul Chiusano,Rúnar Bjarnason,株式会社クイープ
- 出版社/メーカー: インプレス
- 発売日: 2015/03/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (7件) を見る
概要
Scalaで関数型プログラミングを学ぶ本。 関数型と聞くと「ウ"ッ」っとなるプログラマーだが、会社で輪読会のお誘いがあったのと、対象読者が「Javaばっかしやってた人」だったのでなんかできそうだなと思い読んでみことにした。継続的にkotlinをやっているのでScalaで関数型を学べるならこれ幸い。という気持ちもあった。
今回は、1章:関数型プログラムの基礎 のまとめ。
隔週で輪読会をやっているので(可能であれば)毎回ブログにまとめていく
TL;DR
式eがあり、すべてのプログラムpにおいて、pの意味に影響を与えることなく、p内のすべてのeをeの評価結果と置き換えることができるとしたら、eは参照透過です。関数fがあり、式f(x)が参照透過なすべてのxに対して参照透過であるとしたら、fは純粋関数です。
1. 副作用とは
- 関数型プログラミング -> 純粋関数だけを使ってプログラムを構築すること
- 純粋関数 -> 副作用のない関数のこと
- 副作用 -> 単に結果を返すこと意外に何かする関数
副作用の例
- 変数を変更する
- データ構造を直接変更する
- オブジェクトのフィールドを設定する
- 例外をスローする、またはエラーで停止する
- コンソールに出力する、またはユーザー入力を読み取る
- ファイルを読み取る、またはファイルに書き込む
- 画面上ここに脚注を書きます))描画する
副作用を持つコードの例
2. 副作用の排除
- buyCoffeeの際にトランザクションを発生させたくない
- テスタビリティー
- 再利用性
- Chargeを返すように修正
- n杯のCoffeeの購入に対応した
buyCoffees
を作成 - ワンライナーでまとめられるよ!
3. 参照透過性
- どのようなプログラムにおいても、プログラムの意味を書き換えることなく式をその結果に置き換えることができる
intToString(5)
の結果を呼び出し元において"5"
と置き換えても正しく動作する。てきなこれは参照透過なのだろうか?
chargeはUnit
- buyCoffeeの戻りは
new Coffee()
と同等 - つまり、buyCoffeeの呼び出し元を全て
new Coffee()
と置き換えても、正しく動作(影響を与えない)しなければならない - この条件が満たされないので上記buyCoffeeには参照透過性がない。といえる
- buyCoffeeの戻りは
置換モデル
- 参照透過性では、関数が実行する全てのことが戻り値によって表される <= 普遍条件!
- 関数を全て結果に置き換えても同じように計算が進められる。
- 等価による置換
- プログラムの等式推論がなりたつ(意味不明)
純粋性
- 参照透過性が担保されるとめっちゃモジュール性高い
- 合成可能
4. まとめ
- 関数型プログラミングとは副作用のない純粋関数だけを用いてプログラミングすること
- メリット
- モジュール性向上
- 推論の容易可