Writerモナドの使い道 [実行回数]
def hoge(): Writer[Int, String] = for { _ <- Writer.tell(1) r <- Writer.value[Int, String]("hoge") } yield r val w: Writer[Int, List[String]] = List.range(0, 100).map(_ => hoge()).sequence println(w.run) // (hoge()実行回数, 結果のリスト)
Writerモナドの使い道 [ログ出力]
def selectIdFromDb(): Writer[List[String], Int] = for { _ <- Writer.tell(List("db access start.")) _ <- Writer.tell(List("TODO: db access 実装")) r <- Writer.value[List[String], Int](1) _ <- Writer.tell(List("db access end.")) } yield r def getInfoFromApiById(id: Int): Writer[List[String], String] = for { _ <- Writer.tell(List("db access start.")) _ <- Writer.tell(List("TODO: api access 実装")) r <- Writer.value[List[String], String](s"api info. id=[$id]") _ <- Writer.tell(List("db access end.")) } yield r val result = for { id <- selectIdFromDb() info <- getInfoFromApiById(id) } yield info // ログ部分と結果部分に別れる val (logs, infoResult) = result.run // ログ出力 logs.foreach(println) /** * db access start. * TODO: db access 実装 * db access end. * db access start. * TODO: api access 実装 * db access end. */ // 結果出力 println(infoResult) // api info. id=[1]
プログラミングhaskell 8章 version scala 〜後半〜
前半はこちら
def token[T](p: Parser[T]): Parser[T] = for { _ <- space v <- p _ <- space } yield v def identifier: Parser[String] = token(ident) def natural: Parser[Int] = token(nat) def symbol(s: String): Parser[String] = token(string(s)) println((for { _ <- symbol("[") n <- natural nx <- many(for { _ <- symbol(",") lr <- natural } yield lr) _ <- symbol("]") } yield n :: nx).run(" [ 1, 2, 3,4, 5 ]")) def expr: Parser[Int] = for { t <- term r <- (for { _ <- symbol("+") e <- expr } yield t + e) +++ (for { _ <- symbol("-") e <- expr } yield t - e) +++ pure(t) } yield r def term: Parser[Int] = for { f <- factor r <- (for { _ <- symbol("*") t <- term } yield f * t) +++ (for { _ <- symbol("/") t <- term } yield f / t) +++ pure(f) } yield r def factor: Parser[Int] = (for { _ <- symbol("(") e <- expr _ <- symbol(")") } yield e) +++ natural def eval(s: String): Int = expr.run(s).map(_._2).get println(eval("2*3+4")) println(eval("2*(3+4)")) println(eval("(1 + 2) * (7 - 2) / 5"))
プログラミングhaskell 8章 version scala 〜前半〜
type Parser[T] = StateT[Option, String, T] def pure[T](x: T): Parser[T] = StateT.pure[Option, String, T](x) def failure: Parser[String] = StateT[Option, String, String](_ => None) def item: Parser[String] = StateT{ case "" => None case xs => Some(xs.tail, xs.head.toString) } implicit class ParserOps[T](p: Parser[T]) { def +++(q: Parser[T]): Parser[T] = StateT[Option, String, T] { inp => p.run(inp) orElse q.run(inp) } } println((item +++ pure("d")).run("abc")) println((failure +++ pure("d")).run("abc")) println((failure +++ failure).run("abc")) def item2 = for { s1 <- item _ <- item s2 <- item } yield s1 + s2 println(item2.run("abcd")) def sat(p: String => Boolean): Parser[String] = for { x <- item r <- if (p(x)) pure(x) else failure } yield r def digit = sat(_.matches("^\\d$")) def lower = sat(_.matches("^[a-z]$")) def upper = sat(_.matches("^[A-Z]$")) def letter = sat(_.matches("^[a-zA-Z]$")) def alphanum = sat(_.matches("^[0-9a-zA-Z]$")) def char(x: String) = sat(_ == x) println(sat(x => x.matches("^1$")).run("22")) def string(str: String): Parser[String] = str match { case "" => pure("") case _ => for { x <- char(str.head.toString) xs <- string(str.tail.mkString) } yield x + xs } println(string("abc").run("abcdef")) def many[T](p: Parser[T]): Parser[List[T]] = many1(p) +++ pure(Nil) def many1[T](p: Parser[T]): Parser[List[T]] = for { v <- p vs <- many(p) } yield v :: vs println(many(digit).run("123abc")) println(many(digit).run("abcdef")) def ident: Parser[String] = for { x <- lower xs <- many(alphanum) } yield x + xs.mkString def nat: Parser[Int] = many1(digit).map(_.mkString.toInt) def space: Parser[String] = for { _ <- many(sat(_ == " ")) r <- pure("") } yield r println(nat.run("123abc")) println(space.run(" abc"))
Validated(Applicative)のsequence
ListのEitherのsequenceは最初のエラーだけしかかえってこない。
ListのValidatedのsequenceはすべてのエラーがかえってくる。
val eithers: List[Either[String, Int]] = List(Right(1), Left("error1"), Right(2), Left("error2"), Right(3)) val validateds: List[ValidatedNel[String, Int]] = eithers.map(_.toValidatedNel) println(eithers.sequence) // Left(error1) println(validateds.sequence) // Invalid(NonEmptyList(error1, error2))
Readerモナドの使い道 [DI]
catsを使用したReaderモナドの使い道例として、DIのサンプル
trait DbAccessor { def select: List[String] def count: Int } object DbAccessorImpl extends DbAccessor { override def select: List[String] = { // TODO: dbAccess処理 List.empty } override def count: Int = { // TODO: dbAccess処理 0 } } object DbAccessorMock extends DbAccessor { override def select: List[String] = List("a", "c", "d") override def count: Int = 5 } val selectR = Reader[DbAccessor, List[String]](db => db.select) val countR = Reader[DbAccessor, Int](db => db.count) val tupleR = for { list <- selectR int <- countR } yield (int, list) // テスト時はmockを使う println(tupleR.run(DbAccessorMock)) // (5,List(a, c, d)) // 実装はdbアクセスする println(tupleR.run(DbAccessorImpl)) // (0,List())