[小ネタ]monocleでネストしたOption値を扱う

monocleにおけるOptionを含む値の操作のメモです。
2020.06.30

はじめに

monocle でOptionを含むcase classの操作をするときの合成をよく忘れるのでそのメモです。

ネストしたOptionの内側の変更をする2パターンの例を示します。

  1. 外側のOptionがないとき、外側の値を作ってから内側の値を操作する
  2. 外側のOptionがあるときだけ、内側の値を操作する

パターン2はそれぞれの階層におけるlensを作っておいて合成することもできます。

コード

import monocle.macros.GenLens
import monocle._
import Monocle._
import cats.implicits._
import monocle.macros.syntax.lens._

object LensExample extends App {

  final case class Root(node: Option[Node])

  final case class Node(name: Option[String])

  //Root以下のNodeに名前をセットする。Nodeがなければ空のノードをセットする
  println(Root(None).lens(_.node).modify(_.getOrElse(Node(None)).lens(_.name).set("New Name".some).some))

  //Root以下のNodeに名前をセットする。Nodeがなければセットしない

  //Nodeがない時
  println(Root(None) &|-> GenLens[Root](_.node) ^<-? some ^|-> GenLens[Node](_.name) set "New Name".some)
  //Nodeがある時
  println(Root(Node(None).some) &|-> GenLens[Root](_.node) ^<-? some ^|-> GenLens[Node](_.name) set "New Name".some)

  //nameを変更
  println(
    Root(Node("name".some).some) &|-> GenLens[Root](_.node) ^<-? some ^|-> GenLens[Node](_.name) modify (_.map(
      _.toUpperCase)))

}

実行結果

//Root以下のNodeに名前をセットする。Nodeがなければ空のノードをセットする
Root(Some(Node(Some(New Name))))
//Root以下のNodeに名前をセットする。Nodeがなければセットしない
//Nodeがない時
Root(None)
//Nodeがある時
Root(Some(Node(Some(New Name))))
//nameを変更
Root(Some(Node(Some(NAME))))

Process finished with exit code 0