【Scala】Cats使ってみた

千葉大学 Advent Calendar 2019の11日目の記事

関数型プログラミング理解したわw → あれなんだっけ
のループになってて、流石にちゃんとやろうと思って、Catsをいじってたら
すんなり理解できた!(いや実は間違えれるのかもしれない?)
頭の中の整理の意味も込めて簡単にアウトプット!

はじめに

  • Catsとは
    Scalaで関数型プログラミングを簡単にやる事ができるライブラリ

  • 関数型プログラミングとは
    副作用をできる限り局所化するプログラム方法だと僕は思う。

    やりたいことの処理の流れは同じだが、
    試験時と本番時で使用するデーターベースが違うみたいな事がある。
    その時、使用するデーターベースに寄らない流れの部分をあらかじめ書いておき、
    実際に使用する際に、試験用か本番用のデータベースを指定する。

    こうやって副作用を分離する事で、コードの変更箇所が少なかったり、バクがおきているところの特定が早くなったりする。

Catsには多くの機能があるので今回は

  1. OptionT
  2. 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}が返ってくる。

具体例を見てみよう。

  1. データベースから特定のIdの人を抽出し、その名前を取得すると言う流れを定義する
def findById(userId: Long) =
    Kleisli { db: DBSession =>
            db.filter(_.id == userId).map(_.name): Future[String]
        }
  1. どのデーターベースを使うかを指定して、実行する
val devDbSession = DBSession("dev")
val proDbSession = DBSession("pro")

findById(10L).run(devDbSession): Future[String]
findById(10L).run(proDbSession): Future[String]

試験か開発のどちらのデーターベースを使うかと言うことと、処理の流れがしっかり分かれている事がわかる!

まとめ

  • OptionTを使う事で、包まれているOptionをあたかも包まれていない、ただのOptionとして操作する事ができる。

  • Kleisliを使う事で処理の流れと副作用を分離でき、関数型プログラミングが簡単にできる。

コメント

タイトルとURLをコピーしました