千葉大学 Advent Calendar 2019の11日目の記事
関数型プログラミング理解したわw → あれなんだっけ
のループになってて、流石にちゃんとやろうと思って、Catsをいじってたら
すんなり理解できた!(いや実は間違えれるのかもしれない?)
頭の中の整理の意味も込めて簡単にアウトプット!
はじめに
-
Catsとは
Scalaで関数型プログラミングを簡単にやる事ができるライブラリ -
関数型プログラミングとは
副作用をできる限り局所化するプログラム方法だと僕は思う。やりたいことの処理の流れは同じだが、
試験時と本番時で使用するデーターベースが違うみたいな事がある。
その時、使用するデーターベースに寄らない流れの部分をあらかじめ書いておき、
実際に使用する際に、試験用か本番用のデータベースを指定する。こうやって副作用を分離する事で、コードの変更箇所が少なかったり、バクがおきているところの特定が早くなったりする。
Catsには多くの機能があるので今回は
- OptionT
- Kleisli
を使った際のことを書こうと思う。
OptionT
Future[Option[String]]
みたいないくつもの層になっている時に役に立つのがOptionT
である。
下のコードを見てもらうとわかるが、最下層に変更を加えようとすると、
mapなどを多用しないといけなくなる。
val name = Future.successful(Some("Bamboo"))
val addLastName = name.map(_.map(_ + "Tuna"))
// addLastName = Future(Some("BambooTuna"))
これをOptionTを使うと
val nameT: OptionT[Future, String] = OptionT(name)
val addLastNameT = name.map(_ + "Tuna")
// addLastNameT = Future(Some("BambooTuna"))
mapの使用を一つ減らす事ができた!
ただのOptionの操作と同じ感覚で、
Futureに包まれているOptionの操作を行う事ができる。
これは良さそう?
特にNull許可があるデーターベースからデータを取得する際に使えそう。
また以下の3つは全て同じものとなる。操作する際に便利なだけではなく、生成時も便利なメソッドが用意されている。
OptionT(Future.successful(Some("Bamboo")))
OptionT.liftF(Future.successful("Bamboo"))
OptionT.fromOption[Future](Some("Bamboo"))
Kleisli
Kleisliは3つの型パラメーターをとるKleisli[F[_], A, B]
実行する際にA
を渡す事で、結果としてF{B}
が返ってくる。
今回は例としてKleisli[Future, DBSession, String]
を考えると
実行する際にDBSession
(どのデーターベースを使うか)を渡す事で、
結果としてFuture{String}
が返ってくる。
具体例を見てみよう。
- データベースから特定のIdの人を抽出し、その名前を取得すると言う流れを定義する
def findById(userId: Long) =
Kleisli { db: DBSession =>
db.filter(_.id == userId).map(_.name): Future[String]
}
- どのデーターベースを使うかを指定して、実行する
val devDbSession = DBSession("dev")
val proDbSession = DBSession("pro")
findById(10L).run(devDbSession): Future[String]
findById(10L).run(proDbSession): Future[String]
試験か開発のどちらのデーターベースを使うかと言うことと、処理の流れがしっかり分かれている事がわかる!
まとめ
-
OptionTを使う事で、包まれているOptionをあたかも包まれていない、ただのOptionとして操作する事ができる。
-
Kleisliを使う事で処理の流れと副作用を分離でき、関数型プログラミングが簡単にできる。
コメント