├── .gitignore ├── .scalafmt.conf ├── README.md ├── base └── src │ └── main │ └── scala │ └── arm │ └── ArmUtils.scala ├── build.sbt ├── cats └── src │ └── main │ └── scala │ ├── ApplicativeMain.scala │ ├── CatsEffect.scala │ ├── CatsTypeClasses.scala │ ├── ConsoleMonad.scala │ ├── EvalMonad.scala │ ├── FreeApplicative.scala │ ├── FreeMonad.scala │ ├── IdMonad.scala │ ├── KleisliMonad.scala │ ├── ReaderMonad.scala │ ├── StateMonad.scala │ ├── TrampolineMain.scala │ ├── ValidatedMain.scala │ ├── WriterMonad.scala │ └── manual │ ├── CatsMonoidManual.scala │ ├── EitherTransformers.scala │ ├── ManualWriterMonad.scala │ └── OptionTransformers.scala ├── circe └── src │ └── main │ └── scala │ └── CirceMain.scala ├── doobie └── src │ └── main │ ├── resources │ └── logback.xml │ └── scala │ ├── DoobieIOApp.scala │ └── DoobieMain.scala ├── finch └── src │ └── main │ └── scala │ └── FinchMain.scala ├── fs2 └── src │ └── main │ └── scala │ └── Fs2Main.scala ├── http4s └── src │ └── main │ ├── resources │ └── logback.xml │ └── scala │ ├── Http4sMain.scala │ └── PartialFunctions.scala ├── iteratees └── src │ └── main │ └── scala │ └── iteratee │ └── IterateeMain.scala ├── license.txt ├── misc └── src │ └── main │ └── scala │ └── StackSafetyDemo.scala ├── monix └── src │ └── main │ └── scala │ ├── CoevalMain.scala │ ├── MonixTaskMain.scala │ └── ObservableMain.scala ├── monocle └── src │ └── main │ └── scala │ └── MonocleMain.scala ├── project ├── build.properties └── plugins.sbt ├── scalaz └── src │ └── main │ └── scala │ ├── ScalazActorMain.scala │ ├── ScalazDisjunction.scala │ ├── ScalazMain.scala │ └── akka │ └── AkkaActorMain.scala ├── shapeless └── src │ └── main │ └── scala │ ├── CodecDerivation.scala │ ├── HListMain.scala │ └── ShapelessMain.scala └── typeclasses └── src └── main └── scala ├── TypeClasses.scala └── macros └── SimulacrumTypeClasses.scala /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | .vscode 4 | .bsp 5 | citylots.json 6 | companies.json 7 | world.sql 8 | *.class 9 | .ensime 10 | .ensime_cache -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 2.7.5 2 | maxColumn = 120 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mastering Advanced Scala 2 | 3 | This is the companion code repository for the [Mastering Advanced Scala](https://leanpub.com/mastering-advanced-scala) book published by Leanpub. 4 | 5 | ## Description 6 | 7 | The code is organized into several subprojects. Each subproject corresponds to one specific chapter in the book and contains source files of featured standalone examples: 8 | 9 | |subproject|featured library|chapter|status| 10 | |--------|-------------|------|------| 11 | |`typeclasses`|[Simulacrum](https://github.com/mpilquist/simulacrum)|Advanced language features|published| 12 | |`scalaz`|[ScalaZ](https://github.com/scalaz/scalaz)|Exploring ScalaZ|published| 13 | |`cats`|[Cats](https://github.com/typelevel/cats)|Exploring Cats|published| 14 | |`iteratees`|[iteratee.io](https://github.com/travisbrown/iteratee)|Understanding iteratees|published| 15 | |`monix`|[Monix](https://github.com/monixio/monix)|Working with asynchronous code|published| 16 | |`monocle`|[Monocle](https://github.com/julien-truffaut/Monocle/)|Lenses and other optics|published| 17 | |`fs2`|[FS2](https://github.com/functional-streams-for-scala/fs2)|Stream processing|published| 18 | |`circe`|[Circe](https://github.com/travisbrown/circe)|Working with JSON|published| 19 | |`shapeless`|[Shapeless](https://github.com/milessabin/shapeless)|Generic programming with Shapeless|published| 20 | |`finch`|[Finch](https://github.com/finagle/finch)|Purely functional HTTP services|published| 21 | |`http4s`|[http4s](https://github.com/http4s/http4s)|Purely functional HTTP services|published| 22 | |`doobie`|[doobie](https://github.com/tpolecat/doobie)|Database access with doobie|published| 23 | 24 | Also, we're using [Cats Effect](https://typelevel.org/cats-effect/) throughout the book for capturing effects, [Spire](https://github.com/non/spire) for precise arithmetic and [ScalaTags](https://github.com/lihaoyi/scalatags) for constructing HTML pages. 25 | -------------------------------------------------------------------------------- /base/src/main/scala/arm/ArmUtils.scala: -------------------------------------------------------------------------------- 1 | package arm 2 | 3 | 4 | 5 | /** 6 | * Created by denis on 9/2/16. 7 | */ 8 | object ArmUtils { 9 | 10 | import java.io.Closeable 11 | def usingCloseable[A](closeable: Closeable)(block: => A): A = { 12 | try { 13 | block 14 | } finally { 15 | closeable.close() 16 | } 17 | } 18 | def using[A, C <: { def close() }](closeable: C)(block: => A): A = { 19 | try { 20 | block 21 | } finally { 22 | closeable.close() 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := """advanced-scala-code""" 2 | 3 | val monocleVersion = "1.5.1-cats" 4 | val ioIterateeVersion = "0.18.0" 5 | val fs2Version = "1.0.0" 6 | val circeVersion = "0.10.0" 7 | val finchVersion = "0.25.0" 8 | val http4sVersion = "0.20.0-M2" 9 | val doobieVersion = "0.6.0" 10 | val monixVersion = "3.0.0-RC2" 11 | val catsVersion = "1.4.0" 12 | val catsEffectVersion = "1.0.0" 13 | val scalazVersion = "7.2.27" 14 | val ahcVersion = "2.6.0" 15 | val akkaVersion = "2.5.17" 16 | val shapelessVersion = "2.3.3" 17 | val spireVersion = "0.16.0" 18 | val simulacrumVersion = "0.14.0" 19 | val scalaTagsVersion = "0.6.7" 20 | val logbackVersion = "1.2.3" 21 | val macroParadiseVersion = "2.1.0" 22 | 23 | val commonSettings = Seq( 24 | scalaVersion := "2.12.7", 25 | organization := "com.appliedscala", 26 | version := "1.0-SNAPSHOT" 27 | ) 28 | 29 | val macroParadiseSettings = Seq( 30 | addCompilerPlugin("org.scalamacros" % "paradise" % macroParadiseVersion cross CrossVersion.full) 31 | ) 32 | 33 | 34 | val logbackClassicDep = "ch.qos.logback" % "logback-classic" % logbackVersion 35 | 36 | val catsDependencies = Seq( 37 | "org.typelevel" %% "cats-core" % catsVersion, 38 | "org.typelevel" %% "cats-free" % catsVersion, 39 | "org.typelevel" %% "cats-effect" % catsEffectVersion 40 | ) 41 | 42 | val circeDependencies = Seq( 43 | "io.circe" %% "circe-core", 44 | "io.circe" %% "circe-generic", 45 | "io.circe" %% "circe-parser", 46 | "io.circe" %% "circe-optics" 47 | ).map(_ % circeVersion) 48 | 49 | val iterateeDependencies = Seq( 50 | "io.iteratee" %% "iteratee-core" % ioIterateeVersion, 51 | "io.iteratee" %% "iteratee-monix" % ioIterateeVersion, 52 | "io.iteratee" %% "iteratee-files" % ioIterateeVersion 53 | ) 54 | 55 | val fs2Dependencies = Seq( 56 | "co.fs2" %% "fs2-core" % fs2Version, 57 | "co.fs2" %% "fs2-io" % fs2Version 58 | ) 59 | 60 | val scalazDependencies = Seq( 61 | "org.scalaz" %% "scalaz-core" % scalazVersion, 62 | "org.scalaz" %% "scalaz-concurrent" % scalazVersion, 63 | "com.typesafe.akka" %% "akka-actor" % akkaVersion 64 | ) 65 | 66 | val finchDependencies = Seq( 67 | "com.github.finagle" %% "finch-core" % finchVersion, 68 | "com.github.finagle" %% "finch-circe" % finchVersion, 69 | "io.circe" %% "circe-generic" % circeVersion, 70 | "io.circe" %% "circe-parser" % circeVersion 71 | ) 72 | 73 | val http4sDependencies = Seq( 74 | "org.http4s" %% "http4s-dsl" % http4sVersion, 75 | "org.http4s" %% "http4s-blaze-server" % http4sVersion, 76 | "org.http4s" %% "http4s-circe" % http4sVersion, 77 | "io.circe" %% "circe-generic" % circeVersion, 78 | logbackClassicDep 79 | ) 80 | 81 | val scalaTagsDependencies = Seq( 82 | "com.lihaoyi" %% "scalatags" % scalaTagsVersion 83 | ) 84 | 85 | val typeclassesDependencies = Seq( 86 | "com.github.mpilquist" %% "simulacrum" % simulacrumVersion 87 | ) 88 | 89 | val monixDependencies = Seq( 90 | "io.monix" %% "monix" % monixVersion 91 | ) 92 | 93 | val ahcDependencies = Seq( 94 | "org.asynchttpclient" % "async-http-client" % ahcVersion, 95 | logbackClassicDep 96 | ) 97 | 98 | val monocleDependencies = Seq( 99 | "com.github.julien-truffaut" %% "monocle-core" % monocleVersion, 100 | "com.github.julien-truffaut" %% "monocle-macro" % monocleVersion, 101 | "org.typelevel" %% "spire" % spireVersion 102 | ) 103 | 104 | val shapelessDependencies = Seq( 105 | "com.chuusai" %% "shapeless" % shapelessVersion 106 | ) 107 | 108 | val doobieDependencies = Seq( 109 | "org.tpolecat" %% "doobie-core" % doobieVersion, 110 | "org.tpolecat" %% "doobie-postgres" % doobieVersion, 111 | "org.tpolecat" %% "doobie-hikari" % doobieVersion, 112 | logbackClassicDep 113 | ) 114 | 115 | lazy val root = (project in file(".")).settings(commonSettings).aggregate( 116 | typeclasses, cats, iteratees, monocle, fs2, circe, shapeless, finch, scalaz, http4s, monix, base, doobie) 117 | 118 | lazy val typeclasses = (project in file("typeclasses")).settings(commonSettings ++ macroParadiseSettings).settings(libraryDependencies ++= typeclassesDependencies) 119 | 120 | lazy val cats = (project in file("cats")).settings(commonSettings).settings(libraryDependencies ++= (catsDependencies ++ scalazDependencies)) 121 | 122 | lazy val iteratees = (project in file("iteratees")).settings(commonSettings).settings(libraryDependencies ++= iterateeDependencies) 123 | 124 | lazy val monocle = (project in file("monocle")).settings(commonSettings).settings(libraryDependencies ++= (monocleDependencies ++ catsDependencies)) 125 | 126 | lazy val fs2 = (project in file("fs2")).settings(commonSettings).settings(libraryDependencies ++= fs2Dependencies) 127 | 128 | lazy val circe = (project in file("circe")).settings(commonSettings).settings(libraryDependencies ++= (circeDependencies ++ fs2Dependencies)) 129 | 130 | lazy val shapeless = (project in file("shapeless")).settings(commonSettings).settings(libraryDependencies ++= shapelessDependencies) 131 | 132 | lazy val finch = (project in file("finch")).settings(commonSettings).settings(libraryDependencies ++= (finchDependencies ++ scalaTagsDependencies ++ ahcDependencies)) 133 | 134 | lazy val http4s = (project in file("http4s")).settings(commonSettings).settings(libraryDependencies ++= (http4sDependencies ++ scalaTagsDependencies ++ ahcDependencies)) 135 | 136 | lazy val scalaz = (project in file("scalaz")).settings(commonSettings).settings(libraryDependencies ++= (scalazDependencies ++ ahcDependencies)).dependsOn(base) 137 | 138 | lazy val doobie = (project in file("doobie")).settings(commonSettings).settings(libraryDependencies ++= doobieDependencies) 139 | 140 | lazy val monix = (project in file("monix")).settings(commonSettings).settings(libraryDependencies ++= (monixDependencies ++ ahcDependencies)).dependsOn(base) 141 | 142 | lazy val misc = (project in file("misc")).settings(commonSettings) 143 | 144 | lazy val base = (project in file("base")).settings(commonSettings) 145 | -------------------------------------------------------------------------------- /cats/src/main/scala/ApplicativeMain.scala: -------------------------------------------------------------------------------- 1 | import scala.annotation.tailrec 2 | 3 | 4 | /** 5 | * Created by denis on 8/12/16. 6 | */ 7 | object ApplicativeMain { 8 | 9 | case class Cell[A](value: A) { 10 | def map[B](f: A => B): Cell[B] = Cell(f(value)) 11 | } 12 | 13 | def main(args: Array[String]) { 14 | 15 | { 16 | import cats.Functor 17 | import cats.instances.option._ 18 | def increment(i: Int): Int = i + 1 19 | val maybeNum: Option[Int] = Some(42) 20 | val maybeRes = Functor[Option].map(maybeNum)(increment) 21 | println(maybeRes) 22 | // prints Some(43) 23 | } 24 | 25 | 26 | 27 | def add(i: Int, j: Int): Int = i + j 28 | val a: Option[Int] = Some(42) 29 | val b: Option[Int] = Some(33) 30 | 31 | { 32 | import cats.Monad 33 | import cats.instances.option._ 34 | 35 | val maybeRes = Monad[Option].flatMap(a) { aa => 36 | Monad[Option].map(b) { bb => 37 | add(aa, bb) 38 | } 39 | } 40 | println(maybeRes) 41 | // prints Some(75) 42 | } 43 | 44 | // lifting 2-argument function for Option 45 | import cats.Applicative 46 | import cats.instances.option._ 47 | val result = Applicative[Option].map2(a, b)(add) 48 | println(result) 49 | // prints Some(75) 50 | 51 | val c1 = Cell(42) 52 | val c2 = Cell(33) 53 | 54 | { 55 | import cats.Monad 56 | implicit val cellMonad = new Monad[Cell] { 57 | override def flatMap[A, B](fa: Cell[A])(f: (A) => Cell[B]): Cell[B] = fa.map(f).value 58 | override def pure[A](x: A): Cell[A] = Cell(x) 59 | @tailrec override def tailRecM[A, B](a: A)(f: (A) => Cell[Either[A, B]]): 60 | Cell[B] = f(a) match { 61 | case Cell(Left(a1)) => tailRecM(a1)(f) 62 | case Cell(Right(next)) => pure(next) 63 | } 64 | } 65 | 66 | import cats.syntax.flatMap._ 67 | import cats.syntax.functor._ 68 | val result = c1.flatMap(cc1 => c2.map(cc2 => add(cc1, cc2))) 69 | println(result) 70 | // prints Some(75) 71 | } 72 | 73 | 74 | 75 | implicit val cellApplicative = new Applicative[Cell] { 76 | override def pure[A](x: A): Cell[A] = Cell(x) 77 | override def ap[A, B](ff: Cell[(A) => B])(fa: Cell[A]): Cell[B] = { 78 | fa.map(ff.value) 79 | } 80 | } 81 | val resultC = Applicative[Cell].map2(c1, c2)(add) 82 | println(resultC) 83 | // prints Cell(75) 84 | 85 | // alternative syntax 86 | import cats.syntax.apply._ 87 | val c3 = (c1, c2).mapN(add) 88 | println(c3) 89 | // prints Cell(75) 90 | 91 | val tuple = (c1, c2).tupled 92 | println(tuple) 93 | // prints Cell((42,33)) 94 | 95 | traverseTest() 96 | 97 | disjunctions() 98 | } 99 | 100 | def disjunctions(): Unit = { 101 | import scalaz._ 102 | import scalaz.std.list._ 103 | import scalaz.syntax.traverse._ 104 | 105 | val list: List[\/[Exception, String]] = List(\/-("joe"), \/-("lisa")) 106 | val dis = list.sequence 107 | println(dis) 108 | } 109 | 110 | def traverseTest(): Unit = { 111 | case class Person(username: String, firstName: String) 112 | def findByName(username: String): Option[Person] = { 113 | Some(Person(username, username.capitalize)) 114 | } 115 | val usernames = List("joe", "lisa", "ann", "kate") 116 | 117 | import cats.Traverse 118 | import cats.instances.list._ 119 | import cats.instances.option._ 120 | val maybeList = Traverse[List].traverse(usernames)(findByName) 121 | println(maybeList) 122 | 123 | // sequence = traverse(identity) 124 | import cats.syntax.traverse._ 125 | val maybeListAlt = usernames.map(findByName).sequence 126 | 127 | val maybeListAlt2 = Traverse[List].traverse(usernames.map(findByName))(identity) 128 | 129 | println(maybeList) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /cats/src/main/scala/CatsEffect.scala: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.Executors 2 | 3 | import cats.effect.IO 4 | 5 | import scala.concurrent.ExecutionContext 6 | 7 | object PrintThread { 8 | def execute[A](block: => A): A = { 9 | print(s"[${Thread.currentThread().getName}] ") 10 | block 11 | } 12 | } 13 | 14 | object CatsEffect { 15 | def main(args: Array[String]): Unit = { 16 | val blockingService = Executors.newCachedThreadPool() 17 | val blockingCtx = ExecutionContext.fromExecutor(blockingService) 18 | val global = ExecutionContext.global 19 | implicit val contextShift = IO.contextShift(global) 20 | 21 | val ioa: IO[Unit] = for { 22 | _ <- contextShift.shift 23 | _ <- IO { PrintThread.execute(println("Enter your name: ")) } 24 | name <- contextShift.evalOn(blockingCtx)( 25 | IO{ PrintThread.execute(scala.io.StdIn.readLine()) } 26 | ) 27 | _ <- IO { PrintThread.execute(println(s"Hello $name!")) } 28 | _ <- IO { PrintThread.execute(blockingService.shutdown()) } 29 | } yield () 30 | 31 | ioa.unsafeRunSync() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /cats/src/main/scala/CatsTypeClasses.scala: -------------------------------------------------------------------------------- 1 | import cats.{Functor, Monad} 2 | import cats.kernel.Monoid 3 | 4 | import scala.annotation.tailrec 5 | 6 | 7 | 8 | case class Cell[A](value: A) { 9 | def bind[B](f: A => Cell[B]): Cell[B] = f(value) 10 | } 11 | 12 | 13 | 14 | 15 | object CatsTypeClasses { 16 | def main(args: Array[String]): Unit = { 17 | // 18 | // 19 | // println("Write your name: ") 20 | // val name = StdIn.readLine() 21 | // println(s"Hello $name") 22 | 23 | 24 | /* 25 | consoleMonad.flatMap(WriteToConsole("Write your name: ")) { _ => 26 | consoleMonad.flatMap(ReadFromConsole()) { name => 27 | consoleMonad.map(WriteToConsole(s"Hello $name")) { _ => 28 | 29 | } 30 | } 31 | }*/ 32 | 33 | /* 34 | 35 | 36 | */ 37 | 38 | 39 | import cats.syntax.flatMap._ 40 | import cats.syntax.functor._ 41 | 42 | 43 | implicit val cellMonad = new Monad[Cell] { 44 | @tailrec override def tailRecM[A, B](a: A)(f: (A) => Cell[Either[A, B]]): 45 | Cell[B] = f(a) match { 46 | case Cell(Left(a1)) => tailRecM(a1)(f) 47 | case Cell(Right(next)) => pure(next) 48 | } 49 | override def flatMap[A, B](fa: Cell[A])(f: (A) => Cell[B]): Cell[B] = fa.bind(f) 50 | override def pure[A](x: A): Cell[A] = Cell(x) 51 | } 52 | 53 | val newCell = Cell(42).flatMap(a => Cell(a + 1)) 54 | println(newCell) 55 | 56 | val c3 = for { 57 | c1 <- Cell(42) 58 | c2 <- Cell(23) 59 | } yield { 60 | c1 + c2 61 | } 62 | 63 | println(c3) 64 | 65 | /* 66 | 67 | import cats.implicits.monadCombineSyntax 68 | 69 | for { 70 | _ <- WriteToConsole("Write your name: ") 71 | name <- ReadFromConsole() 72 | _ <- WriteToConsole(s"Hello $name") 73 | } yield { 74 | 75 | } 76 | */ 77 | 78 | { 79 | import cats.instances.int.catsKernelStdGroupForInt 80 | import cats.instances.map.catsKernelStdMonoidForMap 81 | 82 | val scores = List(Map("Joe" -> 12, "Kate" -> 21), Map("Joe" -> 10)) 83 | val totals1 = Monoid[Map[String,Int]].combine(Map("Joe" -> 12, "Kate" -> 21), Map("Joe" -> 10)) 84 | println(totals1) 85 | 86 | val totals2 = Monoid[Map[String,Int]].combineAll(scores) 87 | println(totals2) 88 | } 89 | 90 | 91 | import cats.instances.string.catsKernelStdMonoidForString 92 | val result = Monoid[String].combineAll(List("a", "b", "cc")) 93 | println(result) 94 | 95 | { 96 | import cats.instances.int.catsKernelStdGroupForInt 97 | import cats.instances.map.catsKernelStdMonoidForMap 98 | val scores = List(Map("Joe" -> 12, "Kate" -> 21), Map("Joe" -> 10)) 99 | val totals = Monoid[Map[String,Int]].combineAll(scores) 100 | println(totals) 101 | } 102 | 103 | 104 | 105 | implicit val cellFunctor: Functor[Cell] = new Functor[Cell] { 106 | def map[A, B](fa: Cell[A])(f: A => B) = fa.map(f) 107 | } 108 | 109 | def greet(name: String): String = s"Hello $name!" 110 | 111 | //FUNCTORS 112 | { 113 | import cats.implicits.catsStdInstancesForOption 114 | val maybeName = Option("Joe") 115 | println(Functor[Option].map(maybeName)(_.length)) 116 | // prints Some(3) 117 | 118 | println(Functor[Option].lift(greet)(maybeName)) 119 | // prints Some(Hello Joe!) 120 | } 121 | 122 | import cats.instances.list.catsStdInstancesForList 123 | val users = List("Joe", "Kate") 124 | println(Functor[List].fproduct(users)(greet).toMap) 125 | // prints Map(Joe -> Hello Joe!, Kate -> Hello Kate!) 126 | 127 | import cats.implicits.catsStdInstancesForOption 128 | val optUsers = List(Some("Joe"), None, Some("Kate")) 129 | val listOptionFunctor = Functor[List].compose(Functor[Option]) 130 | println(listOptionFunctor.map(optUsers)(greet)) 131 | // prints List(Some(Hello Joe!), None, Some(Hello Kate!)) 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /cats/src/main/scala/ConsoleMonad.scala: -------------------------------------------------------------------------------- 1 | import cats.Monad 2 | 3 | import scala.io.StdIn 4 | 5 | /** 6 | * Created by denis on 8/11/16. 7 | */ 8 | trait ConsoleAction[A] { 9 | def bind[B](f: A => ConsoleAction[B]): ConsoleAction[B] 10 | } 11 | 12 | case class ReadFromConsole() extends ConsoleAction[String] { 13 | override def bind[B](f: (String) => ConsoleAction[B]): ConsoleAction[B] = { 14 | val input = StdIn.readLine() 15 | f(input) 16 | } 17 | } 18 | 19 | case class WriteToConsole(output: String) extends ConsoleAction[Unit] { 20 | override def bind[B](f: (Unit) => ConsoleAction[B]): ConsoleAction[B] = { 21 | println(output) 22 | f() 23 | } 24 | } 25 | 26 | case class NopConsole() extends ConsoleAction[Unit] { 27 | override def bind[B](f: (Unit) => ConsoleAction[B]): ConsoleAction[B] = { 28 | f() 29 | } 30 | } 31 | 32 | object ConsoleMonad { 33 | def main(args: Array[String]) { 34 | implicit val consoleMonad = new Monad[ConsoleAction] { 35 | override def flatMap[A, B](fa: ConsoleAction[A])(f: (A) => ConsoleAction[B]): 36 | ConsoleAction[B] = fa.bind(f) 37 | override def pure[A](x: A): ConsoleAction[A] = 38 | NopConsole().asInstanceOf[ConsoleAction[A]] 39 | // Not stack-safe 40 | override def tailRecM[A, B](a: A)(f: (A) => ConsoleAction[Either[A, B]]): 41 | ConsoleAction[B] = flatMap(f(a)) { 42 | case Left(next) => tailRecM(next)(f) 43 | case Right(b) => pure(b) 44 | } 45 | } 46 | 47 | import cats.syntax.flatMap._ 48 | import cats.syntax.functor._ 49 | 50 | for { 51 | _ <- WriteToConsole("Write your name: ") 52 | name <- ReadFromConsole() 53 | _ <- WriteToConsole(s"Hello $name") 54 | } yield () 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cats/src/main/scala/EvalMonad.scala: -------------------------------------------------------------------------------- 1 | import cats.{Eval, Functor, Monad} 2 | 3 | /** 4 | * Created by denis on 8/3/16. 5 | */ 6 | 7 | 8 | object EvalMonad { 9 | 10 | def sum(num: Long): Long = { 11 | if (num > 0) { 12 | sum(num - 1) + num 13 | } else 0 14 | } 15 | 16 | def sumEval(num: Long): Eval[Long] = { 17 | if (num > 0) { 18 | Eval.defer( sumEval(num - 1) ).map(_ + num) 19 | } else Eval.now(0) 20 | } 21 | 22 | def main(args: Array[String]) { 23 | println(sumEval(30000).value) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cats/src/main/scala/FreeApplicative.scala: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Created by denis on 11/20/16. 4 | */ 5 | sealed abstract class ValidationOp[A] 6 | case class Size(size: Int) extends ValidationOp[Boolean] 7 | case object HasNumber extends ValidationOp[Boolean] 8 | 9 | object FreeApplicative { 10 | 11 | def main(args: Array[String]): Unit = { 12 | import cats.free.FreeApplicative 13 | 14 | type Validation[A] = FreeApplicative[ValidationOp, A] 15 | 16 | import cats.free.FreeApplicative.lift 17 | 18 | def size(size: Int): Validation[Boolean] = lift(Size(size)) 19 | val hasNumber: Validation[Boolean] = lift(HasNumber) 20 | 21 | 22 | import cats.syntax.apply._ 23 | val prog: Validation[Boolean] = (size(5), hasNumber).mapN { case (l, r) => l && r} 24 | 25 | 26 | import cats.Id 27 | import cats.arrow.FunctionK 28 | import cats.implicits._ 29 | 30 | // a function that takes a string as input 31 | type FromString[A] = String => A 32 | 33 | 34 | { 35 | val compiler = new FunctionK[ValidationOp, FromString] { 36 | def apply[A](fa: ValidationOp[A]): FromString[A] = str => 37 | fa match { 38 | case Size(size) => str.size >= size 39 | case HasNumber => str.exists(c => "0123456789".contains(c)) 40 | } 41 | } 42 | 43 | val validator = prog.foldMap[FromString](compiler) 44 | // validator: FromString[Boolean] = scala.Function1$$Lambda$3069/2012160550@3162a52e 45 | 46 | validator("1234") 47 | // res7: Boolean = false 48 | 49 | validator("12345") 50 | } 51 | 52 | { 53 | import cats.data.Kleisli 54 | import cats.implicits._ 55 | import scala.concurrent.Future 56 | import scala.concurrent.ExecutionContext.Implicits.global 57 | 58 | // recall Kleisli[Future, String, A] is the same as String => Future[A] 59 | type ParValidator[A] = Kleisli[Future, String, A] 60 | 61 | val parCompiler = new FunctionK[ValidationOp, ParValidator] { 62 | def apply[A](fa: ValidationOp[A]): ParValidator[A] = Kleisli { str => 63 | fa match { 64 | case Size(size) => Future { str.size >= size } 65 | case HasNumber => Future { str.exists(c => "0123456789".contains(c)) } 66 | } 67 | } 68 | } 69 | 70 | val parValidation = prog.foldMap[ParValidator](parCompiler) 71 | } 72 | 73 | { 74 | import cats.data.Const 75 | 76 | type Log[A] = Const[List[String], A] 77 | 78 | val logCompiler = new FunctionK[ValidationOp, Log] { 79 | def apply[A](fa: ValidationOp[A]): Log[A] = fa match { 80 | case Size(size) => Const(List(s"size >= $size")) 81 | case HasNumber => Const(List("has number")) 82 | } 83 | } 84 | 85 | def logValidation[A](validation: Validation[A]): List[String] = 86 | validation.foldMap[Log](logCompiler).getConst 87 | 88 | import cats.syntax.apply._ 89 | logValidation(prog) 90 | logValidation(size(5) *> hasNumber *> size(10)) 91 | logValidation((hasNumber, size(3)).mapN(_ || _)) 92 | } 93 | 94 | { 95 | import cats.data.Tuple2K 96 | import cats.data.Kleisli 97 | import cats.implicits._ 98 | import scala.concurrent.Future 99 | import cats.data.Const 100 | import scala.concurrent.ExecutionContext.Implicits.global 101 | 102 | type ParValidator[A] = Kleisli[Future, String, A] 103 | type Log[A] = Const[List[String], A] 104 | type ValidateAndLog[A] = Tuple2K[ParValidator, Log, A] 105 | 106 | val prodCompiler = new FunctionK[ValidationOp, ValidateAndLog] { 107 | def apply[A](fa: ValidationOp[A]): ValidateAndLog[A] = fa match { 108 | case Size(size) => 109 | val f: ParValidator[Boolean] = Kleisli(str => 110 | Future { str.size >= size }) 111 | val l: Log[Boolean] = Const(List(s"size > $size")) 112 | Tuple2K[ParValidator, Log, Boolean](f, l) 113 | case HasNumber => 114 | val f: ParValidator[Boolean] = Kleisli(str => 115 | Future(str.exists(c => "0123456789".contains(c)))) 116 | val l: Log[Boolean] = Const(List("has number")) 117 | Tuple2K[ParValidator, Log, Boolean](f, l) 118 | } 119 | } 120 | 121 | val prodValidation = prog.foldMap[ValidateAndLog](prodCompiler) 122 | } 123 | 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /cats/src/main/scala/FreeMonad.scala: -------------------------------------------------------------------------------- 1 | import scala.io.StdIn 2 | import scalaz.concurrent.Task 3 | 4 | /** 5 | * Created by denis on 8/11/16. 6 | */ 7 | trait ActionA[A] 8 | case class ReadAction() extends ActionA[String] 9 | case class WriteAction(output: String) extends ActionA[Unit] 10 | 11 | object FreeMonad { 12 | def main(args: Array[String]) { 13 | 14 | import cats.free.Free 15 | type ActionF[A] = Free[ActionA, A] 16 | 17 | import cats.free.Free.liftF 18 | def read(): ActionF[String] = liftF[ActionA, String](ReadAction()) 19 | def write(output: String): ActionF[Unit] = liftF[ActionA, Unit](WriteAction(output)) 20 | 21 | val result = for { 22 | _ <- write("Write your name: ") 23 | name <- read() 24 | res <- write(s"Hello $name") 25 | } yield res 26 | // result: Free[ActionA, Unit] 27 | 28 | import cats.arrow.FunctionK 29 | import cats.{Id, Monad} 30 | 31 | 32 | val idInterpreter: FunctionK[ActionA, Id] = 33 | new FunctionK[ActionA, Id] { 34 | override def apply[A](fa: ActionA[A]): Id[A] = fa match { 35 | case ReadAction() => 36 | val input = StdIn.readLine() 37 | input 38 | case WriteAction(output) => 39 | println(output) 40 | } 41 | } 42 | 43 | def taskInterpreter: FunctionK[ActionA, Task] = 44 | new FunctionK[ActionA, Task] { 45 | def apply[A](fa: ActionA[A]): Task[A] = (fa match { 46 | case ReadAction() => Task.delay { 47 | val input = StdIn.readLine() 48 | input 49 | } 50 | case WriteAction(output) => Task.delay { 51 | println(output) 52 | } 53 | }).asInstanceOf[Task[A]] 54 | } 55 | 56 | implicit val taskMonad = new Monad[Task] { 57 | override def tailRecM[A,B](a: A)(f: A => Task[Either[A,B]]): Task[B] = 58 | Task.suspend(f(a)).flatMap { 59 | case Left(continueA) => tailRecM(continueA)(f) 60 | case Right(b) => Task.now(b) 61 | } 62 | override def flatMap[A, B](fa: Task[A])(f: (A) => Task[B]): 63 | Task[B] = fa.flatMap(f) 64 | override def pure[A](x: A): Task[A] = Task.now(x) 65 | } 66 | 67 | result.foldMap(taskInterpreter).unsafePerformSync 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cats/src/main/scala/IdMonad.scala: -------------------------------------------------------------------------------- 1 | import cats.{Functor, Monad} 2 | 3 | import scala.annotation.tailrec 4 | 5 | /** 6 | * Created by denis on 8/3/16. 7 | */ 8 | object IdMonad { 9 | 10 | type Id[A] = A 11 | 12 | def main(args: Array[String]) { 13 | 14 | val greet = (name: String) => s"Hello $name" 15 | val greeting = Functor[Id].map("Joe")(greet) 16 | 17 | import cats.syntax.flatMap._ 18 | import cats.syntax.functor._ 19 | 20 | implicit val idMonad = new Monad[Id] { 21 | @tailrec override def tailRecM[A, B](a: A)(f: (A) => Id[Either[A, B]]): 22 | Id[B] = f(a) match { 23 | case Left(a1) => tailRecM(a1)(f) 24 | case Right(b) => b 25 | } 26 | override def pure[A](x: A): Id[A] = x 27 | override def flatMap[A, B](fa: Id[A])(f: (A) => Id[B]): Id[B] = f(fa) 28 | } 29 | 30 | val id1: Id[Int] = 42 31 | val id2: Id[Int] = 23 32 | 33 | val resultId = for { 34 | num1 <- id1 35 | num2 <- id2 36 | } yield num1 + num2 37 | 38 | println(resultId) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cats/src/main/scala/KleisliMonad.scala: -------------------------------------------------------------------------------- 1 | 2 | import cats.{Monad, data} 3 | 4 | import scalaz.concurrent.Task 5 | 6 | /** 7 | * Created by denis on 8/3/16. 8 | */ 9 | 10 | 11 | 12 | 13 | object KleisliMonad { 14 | 15 | class AuthService { 16 | def isLogged(name: String): Task[Boolean] = Task { name.length == 3 } 17 | } 18 | class UserService { 19 | def greet(name: String, isLogged: Boolean): Task[String] = Task { 20 | val actualName = if (isLogged) name else "User" 21 | s"Hello $actualName" 22 | } 23 | } 24 | case class Environment(userName: String, userService: UserService, authService: AuthService) 25 | 26 | def main(args: Array[String]) { 27 | 28 | def isLoggedUser = data.ReaderT[Task, Environment, Boolean] { env => 29 | env.authService.isLogged(env.userName) 30 | } 31 | def greetUser(logged: Boolean) = data.ReaderT[Task, Environment, String] { env => 32 | env.userService.greet(env.userName, logged) 33 | } 34 | 35 | implicit val taskMonad = new Monad[Task] { 36 | def tailRecM[A,B](a: A)(f: A => Task[Either[A,B]]): Task[B] = 37 | Task.suspend(f(a)).flatMap { 38 | case Left(continueA) => tailRecM(continueA)(f) 39 | case Right(b) => Task.now(b) 40 | } 41 | override def flatMap[A, B](fa: Task[A])(f: (A) => Task[B]): Task[B] = fa.flatMap(f) 42 | override def pure[A](x: A): Task[A] = Task.now(x) 43 | } 44 | val resultR = for { 45 | logged <- isLoggedUser 46 | greeting <- greetUser(logged) 47 | } yield greeting 48 | 49 | val environment = Environment("Joe", new UserService, new AuthService) 50 | println(resultR.run(environment).unsafePerformSync) 51 | 52 | // Using Kleisli->local 53 | case class ExternalContext(env: Environment) 54 | val externalContext = ExternalContext(environment) 55 | def context2env(ec: ExternalContext): Environment = ec.env 56 | val resultContextR = resultR.local(context2env) 57 | println(resultContextR.run(externalContext).unsafePerformSync) 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cats/src/main/scala/ReaderMonad.scala: -------------------------------------------------------------------------------- 1 | import cats.Eval 2 | import cats.data.Reader 3 | 4 | /** 5 | * Created by denis on 8/2/16. 6 | */ 7 | 8 | 9 | 10 | 11 | 12 | object ReaderMonad { 13 | 14 | class AuthService { 15 | def isLogged(name: String): Boolean = name.length == 3 16 | } 17 | class UserService { 18 | def greet(name: String, isLogged: Boolean): String = { 19 | val actualName = if (isLogged) name else "User" 20 | s"Hello $actualName" 21 | } 22 | } 23 | case class Environment(userName: String, userService: UserService, authService: AuthService) 24 | 25 | 26 | def main(args: Array[String]) { 27 | val toUpper = Reader((str: String) => str.toUpperCase) 28 | val greet = Reader((name: String) => s"Hello $name") 29 | 30 | val combined1 = toUpper.compose(greet) 31 | val combined2 = toUpper.andThen(greet) 32 | 33 | val result = combined1.run("Joe") 34 | 35 | def isLoggedUser = Reader[Environment, Boolean](env => env.authService.isLogged(env.userName)) 36 | def greetUser(logged: Boolean) = Reader[Environment, String](env => env.userService.greet(env.userName, logged)) 37 | 38 | val resultR = for { 39 | logged <- isLoggedUser 40 | greeting <- greetUser(logged) 41 | } yield greeting 42 | 43 | val environment = Environment("Joe", new UserService, new AuthService) 44 | println(resultR.run(environment)) 45 | 46 | println(result) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cats/src/main/scala/StateMonad.scala: -------------------------------------------------------------------------------- 1 | import cats.data.State 2 | 3 | import scala.util.Random 4 | 5 | /** 6 | * Created by denis on 8/4/16. 7 | */ 8 | object StateMonad { 9 | 10 | 11 | def getIntXorShift(seed: Int): Int = { 12 | var x = seed 13 | x ^= (x << 21) 14 | x ^= (x >>> 35) 15 | x ^= (x << 4) 16 | x 17 | } 18 | 19 | def genRandomChar: Char = { 20 | (Random.nextInt(26) + 65).toChar 21 | } 22 | 23 | def genChar(seed: Int): (Int, Char) = { 24 | val newSeed = getIntXorShift(seed) 25 | val number = Math.abs(newSeed % 25) + 65 26 | (newSeed, number.toChar) 27 | } 28 | 29 | 30 | def main(args: Array[String]) { 31 | 32 | println(List(genRandomChar, genRandomChar, genRandomChar).mkString) 33 | 34 | 35 | def nextChar(seed: Int): (Int, Char) = genChar(seed) 36 | 37 | val initialSeed = 42 38 | val random = { 39 | val (nextSeed1, first) = nextChar(initialSeed) 40 | val (nextSeed2, second) = nextChar(nextSeed1) 41 | val (_, third) = nextChar(nextSeed2) 42 | List(first, second, third) 43 | } 44 | 45 | println(random.mkString) 46 | 47 | 48 | import cats.data 49 | val nextCharS = data.State(genChar) 50 | 51 | val randomS = for { 52 | first <- nextCharS 53 | second <- nextCharS 54 | third <- nextCharS 55 | } yield List(first, second, third) 56 | 57 | 58 | val randomSDS = nextCharS.flatMap( first => 59 | nextCharS.flatMap( second => 60 | nextCharS.map { third => 61 | val result = List(first, second, third) 62 | result 63 | } 64 | ) 65 | ) 66 | 67 | 68 | val result = randomS.runA(initialSeed) 69 | println(result.value.mkString) 70 | 71 | 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /cats/src/main/scala/TrampolineMain.scala: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Created by denis on 8/31/16. 4 | */ 5 | object TrampolineMain { 6 | 7 | sealed trait Op[T] { 8 | def map[D](f: T => D): Op[D] = flatMap(f.andThen { result => Now(result) }) 9 | def flatMap[D](f: T => Op[D]): Op[D] = FlatMap(this, f) 10 | } 11 | case class Now[T](value: T) extends Op[T] 12 | case class Later[T](run: () => Op[T]) extends Op[T] 13 | case class FlatMap[A,B](sub: Op[A], k: A => Op[B]) extends Op[B] 14 | 15 | def main(args: Array[String]): Unit = { 16 | 17 | def sumEval(num: Long): Op[Long] = { 18 | if (num > 0) { 19 | Later( () => sumEval(num - 1) ).map(_ + num) 20 | } else Now(0) 21 | } 22 | 23 | @annotation.tailrec 24 | def run[T](op: Op[T]): T = { 25 | op match { 26 | case Now(t) => t 27 | case Later(f) => run(f()) 28 | case FlatMap(x,f) => x match { 29 | case Now(a) => run(f(a)) 30 | case Later(r) => run(FlatMap(r(), f)) 31 | case FlatMap(y, g) => 32 | run(FlatMap(y, (a: Any) => FlatMap(g(a), f))) 33 | } 34 | } 35 | } 36 | 37 | println(run(sumEval(30000))) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cats/src/main/scala/ValidatedMain.scala: -------------------------------------------------------------------------------- 1 | 2 | import cats.{Applicative, Foldable, Semigroup} 3 | 4 | /** 5 | * Created by denis on 8/13/16. 6 | */ 7 | object ValidatedMain { 8 | 9 | def main(args: Array[String]) { 10 | 11 | case class Person(name: String, email: String) 12 | val person = Person("Joe", "joe@example.com") 13 | 14 | import cats.data.Validated 15 | def checkName(person: Person): Validated[String, String] = 16 | Validated.invalid("The user with this name doesn't exist") 17 | def checkEmail(person: Person): Validated[String, String] = 18 | Validated.invalid("This email looks suspicious") 19 | 20 | import cats.syntax.apply._ 21 | import cats.instances.string.catsKernelStdMonoidForString 22 | val emailCheckV = checkEmail(person) 23 | val nameCheckV = checkName(person) 24 | 25 | val resultV = (emailCheckV, nameCheckV).mapN(_ + _) 26 | resultV.fold( 27 | errors => println(errors), 28 | str => () 29 | ) 30 | 31 | import cats.data.ValidatedNel 32 | type ErrorOr[+A] = ValidatedNel[String, A] 33 | def checkNameNel(person: Person): ErrorOr[String] = 34 | Validated.invalidNel("The user with this name doesn't exist") 35 | def checkEmailNel(person: Person): ErrorOr[String] = 36 | Validated.invalidNel("This email looks suspicious") 37 | 38 | import cats.instances.list.catsKernelStdMonoidForList 39 | val resultNel = (checkEmailNel(person), checkNameNel(person)).mapN(_ + _) 40 | resultNel.fold( 41 | nel => println(nel.toList), 42 | str => () 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cats/src/main/scala/WriterMonad.scala: -------------------------------------------------------------------------------- 1 | import cats.data.{Writer, WriterT} 2 | 3 | /** 4 | * Created by denis on 8/3/16. 5 | */ 6 | object WriterMonad { 7 | 8 | 9 | 10 | def main(args: Array[String]) { 11 | 12 | def greetW(name: String, logged: Boolean) = 13 | Writer(List("Composing a greeting"), { 14 | val userName = if (logged) name else "User" 15 | s"Hello $userName" 16 | }) 17 | def isLoggedW(name: String) = 18 | Writer(List("Checking if user is logged in"), name.length == 3) 19 | 20 | val name = "Joe" 21 | 22 | import cats.instances.list._ 23 | 24 | val resultW = for { 25 | logged <- isLoggedW(name) 26 | greeting <- greetW(name, logged) 27 | } yield greeting 28 | 29 | val (log, result) = resultW.run 30 | println(log) 31 | println(result) 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cats/src/main/scala/manual/CatsMonoidManual.scala: -------------------------------------------------------------------------------- 1 | package manual 2 | 3 | 4 | trait Monoid[A] { 5 | def compose(a: A, b: A): A 6 | def empty: A 7 | } 8 | 9 | object DefaultMonoids { 10 | implicit val stringConcatMonoid = new Monoid[String] { 11 | override def compose(a: String, b: String): String = s"$a$b" 12 | override def empty: String = "" 13 | } 14 | } 15 | 16 | object Operations { 17 | def combineAll[A](list: List[A])(implicit monoid: Monoid[A]): A = { 18 | list.foldRight(monoid.empty)((a, b) => monoid.compose(a, b)) 19 | } 20 | } 21 | 22 | object CatsMonoidManual { 23 | 24 | def main(args: Array[String]): Unit = { 25 | 26 | import DefaultMonoids._ 27 | val result = Operations.combineAll(List("a", "b", "cc")) 28 | println(result) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cats/src/main/scala/manual/EitherTransformers.scala: -------------------------------------------------------------------------------- 1 | package manual 2 | 3 | import cats.{Functor, Monad} 4 | import cats.data.{EitherT} 5 | 6 | import scala.concurrent.Future 7 | import scalaz.concurrent.Task 8 | 9 | /** 10 | * Created by denis on 8/1/16. 11 | */ 12 | class GenerationException(number: Long, message: String) 13 | extends Exception(message) 14 | 15 | object NumberProducer { 16 | import cats.syntax.either._ 17 | def queryNextNumber: Task[Either[GenerationException, Long]] = Task { 18 | Either.catchOnly[GenerationException] { 19 | val source = Math.round(Math.random * 100) 20 | if (source <= 80) source 21 | else throw new GenerationException(source, "The generated number is too big!") 22 | } 23 | } 24 | } 25 | 26 | 27 | object EitherTransformers { 28 | def main(args: Array[String]): Unit = { 29 | 30 | val num1TX = NumberProducer.queryNextNumber 31 | val num2TX = NumberProducer.queryNextNumber 32 | /* 33 | val resultTX = for { 34 | num1X <- num1TX 35 | num2X <- num2TX 36 | } yield { 37 | for { 38 | num1 <- num1X 39 | num2 <- num2X 40 | } yield num1 + num2 41 | } 42 | 43 | val resultX = resultTX.unsafePerformSync 44 | println(s"Result: $resultX")*/ 45 | /* 46 | implicit val taskFunctor = new Functor[Task] { 47 | override def map[A, B](fa: Task[A])(f: (A) => B): Task[B] = fa.map(f) 48 | }*/ 49 | implicit val taskMonad = new Monad[Task] { 50 | def tailRecM[A,B](a: A)(f: A => Task[Either[A,B]]): Task[B] = 51 | Task.suspend(f(a)).flatMap { 52 | case Left(continueA) => tailRecM(continueA)(f) 53 | case Right(b) => Task.now(b) 54 | } 55 | override def flatMap[A, B](fa: Task[A])(f: (A) => Task[B]): Task[B] = fa.flatMap(f) 56 | override def pure[A](x: A): Task[A] = Task.now(x) 57 | } 58 | 59 | val resultTXT = for { 60 | num1 <- EitherT(num1TX) 61 | num2 <- EitherT(num2TX) 62 | } yield num1 + num2 63 | 64 | val resultX = resultTXT.value.unsafePerformSync 65 | println(s"Result: $resultX") 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cats/src/main/scala/manual/ManualWriterMonad.scala: -------------------------------------------------------------------------------- 1 | package manual 2 | 3 | import cats.Monad 4 | 5 | import scala.util.Either 6 | 7 | /** 8 | * Created by denis on 8/4/16. 9 | */ 10 | 11 | case class Writer[V](run: (List[String], V)) { 12 | def bind[B](f: V => Writer[B]): Writer[B] = { 13 | val (log, value) = run 14 | val (nLog, nValue) = f(value).run 15 | Writer((log ++ nLog, nValue)) 16 | } 17 | 18 | def bindNaive[B](f: V => Writer[B]): Writer[B] = { 19 | val (log, value) = run 20 | f(value) 21 | } 22 | } 23 | 24 | 25 | object ManualWriterMonad { 26 | 27 | 28 | def main(args: Array[String]) { 29 | implicit val writerMonad = new Monad[Writer] { 30 | override def pure[A](x: A): Writer[A] = Writer((List(), x)) 31 | override def flatMap[A, B](fa: Writer[A])(f: (A) => Writer[B]): Writer[B] = fa.bind(f) 32 | // Not stack-safe 33 | override def tailRecM[A, B](a: A)(f: (A) => Writer[Either[A, B]]): 34 | Writer[B] = flatMap(f(a)) { 35 | case Right(b) => pure(b) 36 | case Left(nextA) => tailRecM(nextA)(f) 37 | } 38 | } 39 | 40 | def greetW(name: String, logged: Boolean) = 41 | Writer(List("Composing a greeting"), { 42 | val userName = if (logged) name else "User" 43 | s"Hello $userName" 44 | }) 45 | def isLoggedW(name: String) = 46 | Writer(List("Checking if user is logged in"), name.length == 3) 47 | 48 | val name = "Joe" 49 | 50 | import cats.syntax.flatMap._ 51 | import cats.syntax.functor._ 52 | 53 | val resultW = for { 54 | logged <- isLoggedW(name) 55 | greeting <- greetW(name, logged) 56 | } yield greeting 57 | 58 | val (log, result) = resultW.run 59 | println(log) 60 | println(result) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cats/src/main/scala/manual/OptionTransformers.scala: -------------------------------------------------------------------------------- 1 | package manual 2 | 3 | import cats.data.{OptionT, Reader} 4 | 5 | import scala.concurrent.Future 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | 8 | 9 | /** 10 | * Created by denis on 8/1/16. 11 | */ 12 | 13 | 14 | case class FutureOption[A](internal: Future[Option[A]]) { 15 | def map[B](f: A => B): FutureOption[B] = FutureOption { 16 | internal.map(_.map(f)) 17 | } 18 | def flatMap[B](f: A => FutureOption[B]): FutureOption[B] = { 19 | FutureOption { 20 | internal.flatMap(maybeValue => { 21 | maybeValue match { 22 | case Some(value) => f(value).internal 23 | case None => Future.successful(None) 24 | } 25 | }) 26 | } 27 | } 28 | } 29 | 30 | 31 | object OptionTransformers { 32 | 33 | def main(args: Array[String]) { 34 | 35 | def generateNum: Future[Option[Long]] = Future { 36 | val source = Math.round(Math.random * 100) 37 | if (source <= 60) Some(source) else None 38 | } 39 | 40 | val maybeNum1F = generateNum 41 | val maybeNum2F = generateNum 42 | 43 | /* 44 | val resultFO = for { 45 | num1 <- FutureOption(maybeNum1F) 46 | num2 <- FutureOption(maybeNum2F) 47 | } yield num1 + num2 48 | 49 | resultFO.internal.foreach( vv => 50 | println(vv) 51 | )*/ 52 | 53 | import cats.instances.future._ 54 | 55 | val resultFO = for { 56 | num1 <- OptionT(maybeNum1F) 57 | num2 <- OptionT(maybeNum2F) 58 | } yield { 59 | num1 + num2 60 | } 61 | 62 | resultFO.value.foreach( vv => 63 | println(vv) 64 | ) 65 | 66 | 67 | 68 | Thread.sleep(1000) 69 | 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /circe/src/main/scala/CirceMain.scala: -------------------------------------------------------------------------------- 1 | import java.nio.file.Paths 2 | import java.util.concurrent.Executors 3 | 4 | import cats.effect.IO 5 | import fs2.text 6 | import fs2.io.file._ 7 | 8 | import scala.concurrent.ExecutionContext 9 | 10 | /** 11 | * Created by denis on 8/12/16. 12 | */ 13 | object CirceMain { 14 | 15 | def main(args: Array[String]) { 16 | 17 | case class Person(firstName: String, lastName: String, age: Int) 18 | val person = Person("Joe", "Black", 42) 19 | 20 | // Manual 21 | { 22 | import io.circe.Encoder 23 | import io.circe.syntax._ 24 | implicit val personEnc: Encoder[Person] = Encoder.forProduct3( 25 | "firstName", "lastName", "age"){ src => (src.firstName, src.lastName, src.age) } 26 | println(person.asJson) 27 | } 28 | 29 | // Semi-automatic 30 | { 31 | import io.circe.syntax._ 32 | import io.circe.generic.semiauto._ 33 | implicit val personEnc = deriveEncoder[Person] 34 | println(person.asJson) 35 | } 36 | 37 | // Automatic 38 | { 39 | import io.circe.syntax._ 40 | import io.circe.generic.auto._ 41 | println(person.asJson) 42 | } 43 | 44 | val jsonStr = """{ "firstName" : "Joe", "lastName" : "Black", "age" : 42 }""" 45 | 46 | // Manual decoder 47 | { 48 | { 49 | import io.circe.Decoder 50 | import io.circe.jawn._ 51 | implicit val personDecoder: Decoder[Person] = Decoder.forProduct3( 52 | "firstName", "lastName", "age")(Person.apply) 53 | val person = decode[Person](jsonStr) 54 | println(person) 55 | } 56 | 57 | { 58 | import io.circe.Decoder 59 | import io.circe.jawn._ 60 | implicit val personDecoder = for { 61 | firstName <- Decoder.instance(_.get[String]("firstName")) 62 | lastName <- Decoder.instance(_.get[String]("lastName")) 63 | age <- Decoder.instance(_.get[Int]("age")) 64 | } yield Person(firstName, lastName, age) 65 | val person = decode[Person](jsonStr) 66 | println(person) 67 | } 68 | 69 | { 70 | import io.circe.Decoder 71 | import io.circe.jawn._ 72 | import cats.syntax.apply._ 73 | 74 | val firstNameD = Decoder.instance(_.get[String]("firstName")) 75 | val lastNameD = Decoder.instance(_.get[String]("lastName")) 76 | val ageD = Decoder.instance(_.get[Int]("age")) 77 | implicit val personDecoder = (firstNameD, lastNameD, ageD).mapN(Person.apply) 78 | val person = decode[Person](jsonStr) 79 | println(person) 80 | } 81 | } 82 | 83 | // Semi-automatic decoder 84 | { 85 | import io.circe.generic.semiauto._ 86 | import io.circe.jawn._ 87 | implicit val personDec = deriveDecoder[Person] 88 | val person = decode[Person](jsonStr) 89 | println(person) 90 | } 91 | 92 | // Automatic 93 | { 94 | import io.circe.jawn._ 95 | import io.circe.generic.auto._ 96 | val person = decode[Person](jsonStr) 97 | println(person) 98 | } 99 | 100 | // Parsing numbers 101 | { 102 | val jsonStr = 103 | """{ "firstName" : "Joe", "lastName" : "Black", "age" : 42, 104 | |"address": { "street": "Market st.", "city": "Sydney", 105 | |"postal": 2000, "state": "NSW" }, 106 | |"departments": ["dev", "hr", "qa"] }""".stripMargin 107 | 108 | import io.circe.jawn._ 109 | import io.circe.syntax._ 110 | val result = parse(jsonStr) 111 | // result: Either[Error, Json] 112 | 113 | import io.circe.Json 114 | val json = result.getOrElse(Json.Null) 115 | val cursor = json.hcursor 116 | 117 | cursor.downField("departments").downArray.right. 118 | withFocus(_.withString(_.toUpperCase.asJson)) 119 | 120 | val modified = result.map { json => 121 | json.hcursor.downField("age"). 122 | withFocus(_.withNumber(_.toInt.map(n => n + 1).asJson)) 123 | } 124 | 125 | modified.fold( 126 | fail => println(fail.message), 127 | res => println(res.top) 128 | ) 129 | 130 | // Using optics 131 | { 132 | val jsonStr = """{ 133 | "firstName":"Joe", 134 | "address":{ 135 | "street":"Market st.", 136 | "city":"Sydney" 137 | } 138 | }""" 139 | 140 | import io.circe.jawn._ 141 | val result = parse(jsonStr) 142 | val json = result.getOrElse(Json.Null) 143 | 144 | 145 | import io.circe.optics.JsonPath._ 146 | val cityO = root.address.city.string 147 | // cityO: Optional[Json, String] 148 | println(cityO.getOption(json)) 149 | // prints Some(Sydney) 150 | 151 | val secondDepO = root.departments.index(1).string 152 | println(secondDepO.getOption(json)) 153 | // prints Some(hr) 154 | 155 | val updatedJson = cityO.set("Newcastle")(json) 156 | 157 | import io.circe.Printer 158 | val updatedStr = updatedJson.pretty(Printer.spaces2) 159 | println(updatedStr) 160 | } 161 | } 162 | // readingLargeFile() 163 | } 164 | 165 | def readingLargeFile(): Unit = { 166 | // Reading a large file 167 | case class Company(name: String, permalink: String, homepage_url: String) 168 | 169 | import io.circe.jawn._ 170 | import io.circe.generic.auto._ 171 | 172 | implicit val contextShift = IO.contextShift(ExecutionContext.Implicits.global) 173 | val blockingContext = ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) 174 | 175 | // download link: http://jsonstudio.com/wp-content/uploads/2014/02/companies.zip 176 | val filePath = Paths.get("companies.json") 177 | val byteStr = readAll[IO](filePath, blockingContext, 1024) 178 | val lineStr = byteStr.through(text.utf8Decode).through(text.lines) 179 | val resultT = lineStr.map { line => 180 | decode[Company](line) 181 | }.filter(_.isRight).map { company => 182 | company.foreach(println) 183 | company 184 | }.take(5).compile.toVector 185 | 186 | val diff = System.currentTimeMillis() 187 | resultT.unsafeRunSync() 188 | println(s"Elapsed: ${System.currentTimeMillis() - diff}") 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /doobie/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /doobie/src/main/scala/DoobieIOApp.scala: -------------------------------------------------------------------------------- 1 | import cats.effect._ 2 | import cats.implicits._ 3 | import doobie.implicits._ 4 | import doobie.hikari.HikariTransactor 5 | import doobie.util.ExecutionContexts 6 | 7 | object DoobieIOApp extends IOApp { 8 | override def run(args: List[String]): IO[ExitCode] = { 9 | // Resource[IO, HikariTransactor[IO]] 10 | val transactor: Resource[IO, HikariTransactor[IO]] = for { 11 | ce <- ExecutionContexts.fixedThreadPool[IO](32) 12 | te <- ExecutionContexts.cachedThreadPool[IO] 13 | xa <- HikariTransactor.newHikariTransactor[IO]( 14 | "org.postgresql.Driver", "jdbc:postgresql:doobieworld", 15 | "doobieuser", "doobiepass", ce, te) 16 | } yield xa 17 | 18 | val districtC = sql"select district from city where name = 'Canberra'". 19 | query[String].unique 20 | 21 | transactor.use { xa => 22 | for { 23 | district <- districtC.transact(xa) 24 | _ <- IO(println(district)) 25 | } yield ExitCode.Success 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /doobie/src/main/scala/DoobieMain.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by denis on 8/28/16. 3 | */ 4 | object DoobieMain { 5 | import scala.concurrent.ExecutionContext 6 | import doobie._ 7 | import doobie.implicits._ 8 | import cats.implicits._ 9 | import cats.effect.IO 10 | 11 | def main(args: Array[String]) { 12 | // type ConnectionIO[A] = cats.free.Free[connection.ConnectionOp, A] 13 | val helloWorld = "Hello World".pure[ConnectionIO] 14 | 15 | implicit val cs = IO.contextShift(ExecutionContext.global) 16 | val xa = Transactor.fromDriverManager[IO]( 17 | "org.postgresql.Driver", "jdbc:postgresql:doobieworld", 18 | "doobieuser", "doobiepass" 19 | ) 20 | 21 | { 22 | val task = helloWorld.transact(xa).map { str => 23 | println("Thread: " + Thread.currentThread().getName) 24 | str 25 | } 26 | println(task.unsafeRunSync) 27 | } 28 | 29 | { 30 | val yearC = sql"select extract(year from current_date)". 31 | query[Int].unique 32 | val task = yearC.transact(xa) 33 | println(task.unsafeRunSync) 34 | } 35 | 36 | multipleQueriesExample(xa) 37 | shapelessResultExample(xa) 38 | caseClassResultExample(xa) 39 | fs2IntegrationExample(xa) 40 | } 41 | 42 | def multipleQueriesExample(xa: Transactor[IO]): Unit = { 43 | val districtC = sql"select district from city where name = 'Canberra'". 44 | query[String].unique 45 | val populationC = sql"select population from country where name = 'Australia'". 46 | query[Int].unique 47 | 48 | { 49 | val dataC = (districtC, populationC).tupled 50 | val task = dataC.transact(xa) 51 | println(task.unsafeRunSync) 52 | // prints (Capital Region,18886000) 53 | } 54 | 55 | { 56 | val dataC = for { 57 | district <- districtC 58 | population <- populationC 59 | } yield (district, population) 60 | 61 | val task = dataC.transact(xa) 62 | println(task.unsafeRunSync) 63 | } 64 | } 65 | 66 | def caseClassResultExample(xa: Transactor[IO]): Unit = { 67 | def findByCode(code: String) = 68 | sql"""select name, population, headofstate from country where code = $code""". 69 | query[CountryInfo] 70 | 71 | println(findByCode("SMR").sql) 72 | // select name, population, headofstate from country where code = ? 73 | 74 | val task = findByCode("SMR").option.transact(xa) 75 | println(task.unsafeRunSync) 76 | // prints Some(CountryInfo(San Marino,27000,None)) 77 | } 78 | 79 | def shapelessResultExample(xa: Transactor[IO]): Unit = { 80 | import shapeless._ 81 | 82 | def findByCode(code: String) = 83 | sql"""select name, population, headofstate from country where code = $code""". 84 | query[String :: Int :: Option[String] :: HNil] 85 | 86 | println(findByCode("SMR").sql) 87 | // select name, population, headofstate from country where code = ? 88 | 89 | val task = findByCode("SMR").option.transact(xa) 90 | println(task.unsafeRunSync) 91 | // prints Some(San Marino :: 27000 :: None :: HNil) 92 | } 93 | 94 | def fs2IntegrationExample(xa: Transactor[IO]): Unit = { 95 | import fs2._ 96 | 97 | val countryStream: Stream[ConnectionIO, CountryInfo] = 98 | sql"""select name, population, headofstate from country""". 99 | query[CountryInfo].stream 100 | val streamTask = countryStream.transact(xa) 101 | // streamTask: Stream[IO, CountryInfo] 102 | 103 | val filtered = streamTask.filter(_.population < 30000). 104 | map(_.population).fold(0)( (a,b) => a + b ) 105 | val countries = filtered.compile.toVector.unsafeRunSync 106 | println(countries) 107 | // prints Vector(215350) 108 | } 109 | } 110 | 111 | case class CountryInfo(name: String, population: Int, headOfState: Option[String]) -------------------------------------------------------------------------------- /finch/src/main/scala/FinchMain.scala: -------------------------------------------------------------------------------- 1 | import java.util.UUID 2 | 3 | import io.finch._ 4 | import io.finch.syntax.get 5 | 6 | case class Person(name: String, age: Int) 7 | case class PersonInfo(id: Long, firstName: String, lastName: String, age: Int) 8 | 9 | object HtmlEndpoints { 10 | import com.twitter.finagle.http.Response 11 | import com.twitter.io.Buf 12 | 13 | private def htmlResponse(document: String): Response = { 14 | val rep = Response() 15 | rep.content = Buf.Utf8(document) 16 | rep.contentType = "text/html" 17 | rep 18 | } 19 | 20 | object HtmlTemplates { 21 | import scalatags.Text.all._ 22 | 23 | def hello(name: String): String = { 24 | html( 25 | body( 26 | h1(s"Hello ${name.capitalize}!"), 27 | div( 28 | p("Welcome to the show!") 29 | ) 30 | ) 31 | ) 32 | }.toString() 33 | } 34 | 35 | // http://localhost:8080/doc/joe 36 | val docE: Endpoint[Response] = get("doc" :: path[String]) { name: String => 37 | val document = HtmlTemplates.hello(name) 38 | htmlResponse(document) 39 | } 40 | } 41 | 42 | object Endpoints { 43 | import io.finch._ 44 | import io.finch.syntax._ 45 | 46 | // async (Note: code is incorrecly highlighted as error in IntelliJ) 47 | val asyncE = get("async" :: path[Int]).mapAsync { index: Int => 48 | import com.twitter.util.FuturePool 49 | import com.twitter.util.Promise 50 | import io.circe.Json 51 | import io.circe.parser._ 52 | FuturePool.unboundedPool { 53 | import org.asynchttpclient.Dsl._ 54 | val whenResponse = asyncHttpClient. 55 | prepareGet(s"https://jsonplaceholder.typicode.com/todos/$index").execute() 56 | val promise = Promise[Json]() 57 | whenResponse.toCompletableFuture.whenComplete((response, throwable) => { 58 | if (throwable != null) { 59 | promise.setException(throwable) 60 | } else { 61 | val body = response.getResponseBody 62 | promise.setValue(parse(body).getOrElse(Json.Null)) 63 | } 64 | }) 65 | promise 66 | }.flatten 67 | } 68 | 69 | // time 70 | val timeE = get("time") { Ok(System.currentTimeMillis().toString) } 71 | 72 | // http://localhost:8080/greet/joe?id=33 73 | val greetE: Endpoint[String] = get("greet" :: 74 | path[String].shouldNot("be less than two letters"){_.length < 2} :: 75 | param[Int]("id").should("be more than zero"){_ > 0}) { 76 | (userName: String, id: Int) => 77 | if (id % 2 == 0) { 78 | BadRequest(new Exception("ID is wrong!")) 79 | } else { 80 | Ok(s"Hello, ${userName.capitalize}! Your number is #$id") 81 | } 82 | } 83 | 84 | // JSON - http://localhost:8080/data/users?id=33 85 | val personInfoE = get("data" :: "users" :: param[Long]("id")) { id: Long => 86 | Ok(PersonInfo(id, "Joe", "Black", 42)) 87 | } 88 | 89 | implicit val uuidDecoder: DecodeEntity[UUID] = 90 | DecodeEntity.instance { s => 91 | com.twitter.util.Try(UUID.fromString(s)) 92 | } 93 | 94 | val idE: Endpoint[String] = get("id" :: path[UUID]) { id: UUID => 95 | Ok(s"Your UUID has variant ${id.variant()}") 96 | } 97 | } 98 | 99 | object FinchMain { 100 | import com.twitter.finagle.Http 101 | import com.twitter.util.Await 102 | import Endpoints._ 103 | import HtmlEndpoints._ 104 | 105 | def main(args: Array[String]) { 106 | import io.circe.generic.auto._ 107 | import io.finch.circe._ 108 | val api = greetE :+: timeE :+: personInfoE :+: docE :+: idE :+: asyncE 109 | val server = Http.server.serve(":8080", api.toService) 110 | Await.ready(server) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /fs2/src/main/scala/Fs2Main.scala: -------------------------------------------------------------------------------- 1 | import fs2._ 2 | import java.nio.file._ 3 | import java.util.concurrent.Executors 4 | 5 | import cats.effect.IO 6 | 7 | import scala.concurrent.ExecutionContext 8 | import scala.concurrent.duration._ 9 | import scala.concurrent.duration.FiniteDuration 10 | import scala.util.Random 11 | 12 | /** 13 | * Created by denis on 8/7/16. 14 | */ 15 | object Fs2Main { 16 | 17 | implicit val timer = IO.timer(ExecutionContext.global) 18 | implicit val strategy = ExecutionContext.fromExecutor( 19 | Executors.newFixedThreadPool(Runtime.getRuntime.availableProcessors())) 20 | val blockingContext = ExecutionContext. 21 | fromExecutor(Executors.newCachedThreadPool()) 22 | 23 | def log[A](prefix: String): Pipe[IO, A, A] = 24 | _.evalMap(a => IO.apply { println(s"$prefix> $a"); a }) 25 | 26 | def randomDelays[A](max: FiniteDuration): Pipe[IO, A, A] = _.evalMap { a => 27 | val delay = IO.apply(Random.nextInt(max.toMillis.toInt)) 28 | delay.flatMap { d => 29 | IO.sleep(d.millis).flatMap(_ => IO.pure(a)) 30 | } 31 | } 32 | 33 | def main(args: Array[String]) { 34 | fs2FileExample() 35 | } 36 | 37 | private def chunkExample(): Unit = { 38 | import fs2.Chunk 39 | 40 | val ch: Chunk[Int] = Chunk.singleton(42) 41 | val chL = Chunk.longs(Array[Long](1,2,3,4)) 42 | 43 | val stream = Stream.chunk(chL) 44 | 45 | def threadName = Thread.currentThread().getName 46 | val e1 = Stream.eval(IO{ println(s"$threadName"); 1 }) 47 | e1.compile 48 | } 49 | 50 | private def fs2StreamExamples(): Unit = { 51 | implicit val contextShift = IO.contextShift( 52 | ExecutionContext.Implicits.global) 53 | val pureStream = Stream.apply(1, 2, 3) 54 | pureStream.intersperse(1) 55 | 56 | pureStream.compile.toVector 57 | 58 | val e1 = Stream.eval(IO { 59 | println(s"${Thread.currentThread().getName}"); 1 60 | }) 61 | e1.flatMap(value => Stream.eval[IO, Int](IO(value + 1))) 62 | 63 | 64 | val incPipe: Pipe[Pure, Int, Int] = source => source.map(_ + 1) 65 | 66 | val pureStream2 = Stream.emits[IO, Int](Seq(1)) 67 | 68 | val simpleStream = Stream(1, 2, 3).covary[IO] 69 | val pull = simpleStream 70 | val resultPull = pull.map { value => 71 | value + 1 72 | }.through(log("log")).compile.toVector.unsafeRunSync() 73 | println(resultPull) 74 | 75 | import scala.concurrent.duration._ 76 | 77 | val periodic = Stream.awakeEvery[IO](500.milliseconds).flatMap(dur => Stream.eval { 78 | IO { 79 | println(dur.length) 80 | dur.length 81 | } 82 | }).interruptWhen(Stream.sleep[IO](5.seconds).as(true)) 83 | 84 | periodic.compile.toVector.unsafeRunAsync { result => 85 | println(result) 86 | } 87 | 88 | // Thread.sleep(20000) 89 | } 90 | 91 | private def fs2FileExample(): Unit = { 92 | implicit val contextShift = IO.contextShift(ExecutionContext.Implicits.global) 93 | 94 | val filePath = Paths.get("license.txt") 95 | import fs2.io.file._ 96 | val byteStr = readAll[IO](filePath, blockingContext, 1024) 97 | val lineStr = byteStr.through(text.utf8Decode).through(text.lines) 98 | val wordStr = lineStr.flatMap { line => 99 | Stream.emits(line.split("\\W")) 100 | }.map(_.toLowerCase).map(_.trim).filter(!_.isEmpty) 101 | val resultT = wordStr.fold(Map.empty[String, Int]) { (acc, next) => 102 | acc.get(next) match { 103 | case None => acc + (next -> 1) 104 | case Some(num) => acc + (next -> (1 + num)) 105 | } 106 | }.map { dataMap => 107 | dataMap.toList.sortWith( _._2 > _._2).take(5).map(_._1) 108 | }.compile.toVector 109 | 110 | val diff = System.currentTimeMillis() 111 | println(resultT.unsafeRunSync()) 112 | println(s"Elapsed: ${System.currentTimeMillis() - diff}") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /http4s/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /http4s/src/main/scala/Http4sMain.scala: -------------------------------------------------------------------------------- 1 | import java.util.UUID 2 | 3 | import cats.effect.IO 4 | import scala.util.Try 5 | 6 | case class Person(name: String, age: Int) 7 | 8 | object Endpoints { 9 | import org.http4s._ 10 | import org.http4s.dsl.io._ 11 | 12 | val helloWorldService = HttpRoutes.of[IO] { 13 | case GET -> Root / "hello" / IntVar(number) => 14 | Ok(s"Hello, your number is $number") 15 | } 16 | 17 | val asyncRequest = HttpRoutes.of[IO] { 18 | case GET -> Root / "async" => 19 | Ok { 20 | IO.async[String] { eitherCb => 21 | import org.asynchttpclient.Dsl._ 22 | val whenResponse = asyncHttpClient. 23 | prepareGet("https://httpbin.org/get").execute() 24 | whenResponse.toCompletableFuture.whenComplete((res, th) => { 25 | if (th != null) { 26 | eitherCb(Left(th)) 27 | } else eitherCb(Right(res.getResponseBody)) 28 | }) 29 | } 30 | } 31 | } 32 | 33 | val jsonRequest = HttpRoutes.of[IO] { 34 | case GET -> Root / "json" => 35 | import org.http4s.circe._ // EntityEncoder[IO, Json] 36 | import io.circe.generic.auto._ // automatic codecs for Person 37 | import io.circe.syntax._ // asJson method 38 | Ok { 39 | Person("Joe", 42).asJson 40 | } 41 | } 42 | 43 | val idService = HttpRoutes.of[IO] { 44 | case GET -> Root / "id" / UuidVar(id) => 45 | Ok(s"Your ID is $id") 46 | } 47 | 48 | val timeService = HttpRoutes.of[IO] { 49 | case GET -> Root / "time" => 50 | Ok(System.currentTimeMillis().toString) 51 | } 52 | 53 | object UuidVar { 54 | def unapply(s: String): Option[UUID] = { 55 | Try { UUID.fromString(s) }.toOption 56 | } 57 | } 58 | } 59 | 60 | 61 | import cats.effect.{ExitCode, IO, IOApp} 62 | object Http4sMain extends IOApp { 63 | 64 | import Endpoints._ 65 | import cats.implicits._ 66 | import org.http4s.implicits._ 67 | import org.http4s.server.blaze._ 68 | import org.http4s.server.Router 69 | 70 | val api = helloWorldService <+> timeService <+> idService <+> asyncRequest <+> jsonRequest 71 | 72 | val httpApp = Router("/" -> api).orNotFound 73 | 74 | def run(args: List[String]): IO[ExitCode] = 75 | BlazeServerBuilder[IO] 76 | .bindHttp(8080) 77 | .withHttpApp(httpApp) 78 | .serve 79 | .compile 80 | .drain 81 | .as(ExitCode.Success) 82 | } 83 | -------------------------------------------------------------------------------- /http4s/src/main/scala/PartialFunctions.scala: -------------------------------------------------------------------------------- 1 | object PartialFunctions { 2 | def main(args: Array[String]): Unit = { 3 | val pf12: PartialFunction[String, Int] = { 4 | case "one" => 1 5 | case "two" => 2 6 | } 7 | val pf34: PartialFunction[String, Int] = { 8 | case "three" => 3 9 | case "four" => 4 10 | } 11 | val pf1234 = pf12.orElse(pf34) 12 | println(pf1234("four")) // 4 13 | // println(pf1234("five")) // scala.MatchError 14 | println(pf1234.lift("five")) // None 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iteratees/src/main/scala/iteratee/IterateeMain.scala: -------------------------------------------------------------------------------- 1 | package iteratee 2 | 3 | import scala.util.{Failure, Success} 4 | 5 | object IterateeMain { 6 | def fileExample(): Unit = { 7 | import io.iteratee.monix.task._ 8 | import java.io.File 9 | 10 | val wordsE = readLines(new File("license.txt")).flatMap { line => 11 | enumIndexedSeq(line.split("\\W")) 12 | } 13 | val noEmptyLinesEE = filter[String](str => str.trim.length > 0) 14 | val toLowerEE = map[String, String](_.toLowerCase) 15 | val countWordsI = fold[String, Map[String, Int]](Map.empty) { (acc, next) => 16 | acc.get(next) match { 17 | case None => acc + (next -> 1) 18 | case Some(num) => acc + (next -> (1 + num)) 19 | } 20 | } 21 | val dataT = wordsE.through(noEmptyLinesEE). 22 | through(toLowerEE).into(countWordsI).map { dataMap => 23 | dataMap.toList.sortWith( _._2 > _._2).take(5).map(_._1) 24 | } 25 | import monix.execution.Scheduler.Implicits.global 26 | dataT.runOnComplete { 27 | case Success(data) => println(data) 28 | case Failure(th) => th.printStackTrace() 29 | } 30 | 31 | /*val lines = linesEn.through(filterEnee).into(takeI[String](100)).unsafePerformSyncAttempt 32 | 33 | lines.map { lns => 34 | println(lns) 35 | }*/ 36 | } 37 | 38 | 39 | def main(args: Array[String]) { 40 | import io.iteratee.modules.id._ 41 | 42 | // Just one Int 43 | val singleNumE = enumOne(42) 44 | val singleNumI = takeI[Int](1) 45 | val singleNumResult = singleNumE.into(singleNumI) 46 | println(singleNumResult) 47 | 48 | // Incrementing one Int 49 | val incrementNumEE = map[Int, Int](_ + 1) 50 | val incrementedNumResult = singleNumE.through(incrementNumEE).into(singleNumI) 51 | println(incrementedNumResult) 52 | 53 | // First 10 even numbers 54 | val naturalsE = iterate(1)(_ + 1) 55 | val moreThan100EE = filter[Int](_ >= 100) 56 | val evenFilterEE = filter[Int](_ % 2 == 0) 57 | val first10I = takeI[Int](10) 58 | println(naturalsE.through(moreThan100EE).through(evenFilterEE).into(first10I)) 59 | 60 | { 61 | import io.iteratee.modules.eval._ 62 | // Summing N first numbers 63 | val naturalsE = iterate(1)(_ + 1) 64 | val limit1kEE = take[Int](30000) 65 | val sumI = fold[Int, Int](0) { (acc, next) => acc + next } 66 | println(naturalsE.through(limit1kEE).into(sumI).value) 67 | } 68 | 69 | fileExample() 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /misc/src/main/scala/StackSafetyDemo.scala: -------------------------------------------------------------------------------- 1 | import scala.util.control.TailCalls._ 2 | import scala.annotation.tailrec 3 | import scala.concurrent.{Await, Future} 4 | import scala.concurrent.ExecutionContext.Implicits.global 5 | import scala.concurrent.duration.Duration 6 | 7 | object StackSafetyDemo { 8 | 9 | def sum(xs: List[Int]): Int = { 10 | def loopNaive(list: List[Int]): Int = { 11 | list match { 12 | case head :: tail => head + loopNaive(tail) 13 | case Nil => 0 14 | } 15 | } 16 | 17 | def loopTailCall(list: List[Int]): TailRec[Int] = { 18 | list match { 19 | case head :: tail => tailcall(loopTailCall(tail).map(head + _)) 20 | case Nil => done(0) 21 | } 22 | } 23 | 24 | def loopFutureNaive(list: List[Int]): Future[Int] = { 25 | list match { 26 | case head :: tail => loopFutureNaive(tail).map(_ + head) 27 | case Nil => Future.successful(0) 28 | } 29 | } 30 | 31 | def loopFuture(list: List[Int]): Future[Int] = { 32 | list match { 33 | case head :: tail => 34 | Future.unit.flatMap { _ => 35 | loopFuture(tail).map(_ + head) 36 | } 37 | case Nil => Future.successful(0) 38 | } 39 | } 40 | 41 | @tailrec 42 | def loopTailRec(list: List[Int], acc: Int): Int = { 43 | list match { 44 | case head :: tail => loopTailRec(tail, acc + head) 45 | case Nil => acc 46 | } 47 | } 48 | 49 | Await.result(loopFutureNaive(xs), Duration.Inf) 50 | } 51 | 52 | def main(args: Array[String]): Unit = { 53 | val xs = 1.to(10000).toList 54 | println(sum(xs)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /monix/src/main/scala/CoevalMain.scala: -------------------------------------------------------------------------------- 1 | import cats.{Applicative, Cartesian} 2 | 3 | /** 4 | * Created by denis on 8/31/16. 5 | */ 6 | object CoevalMain { 7 | 8 | def main(args: Array[String]): Unit = { 9 | { 10 | import monix.eval.Coeval 11 | val lazyNum = Coeval.evalOnce { println(42); 42 } 12 | println(lazyNum.value) 13 | val exc = Coeval.eval( throw new Exception ) 14 | } 15 | 16 | 17 | { 18 | import monix.eval.Coeval 19 | def sumEval(num: Long): Coeval[Long] = { 20 | if (num > 0) { 21 | Coeval.defer( sumEval(num - 1) ).map(_ + num) 22 | } else Coeval.now(0) 23 | } 24 | println(sumEval(30000).value) 25 | } 26 | 27 | { 28 | // Applicatives 29 | import monix.eval.Coeval 30 | val nameC: Coeval[String] = Coeval.eval("Joe") 31 | val surnameC: Coeval[String] = Coeval.eval("Black") 32 | def add(i: String, j: String): String = s"$i $j" 33 | val sum = Applicative[Coeval].map2(nameC, surnameC)(add) 34 | println(sum.value) 35 | 36 | // Cartesian product 37 | println(Coeval.map2(nameC, surnameC)(add).value()) 38 | import cats.syntax.apply._ 39 | println((nameC, surnameC).mapN(add).value()) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /monix/src/main/scala/MonixTaskMain.scala: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * Created by denis on 9/2/16. 5 | */ 6 | object MonixTaskMain { 7 | 8 | def main(args: Array[String]): Unit = { 9 | import org.asynchttpclient.DefaultAsyncHttpClient 10 | val asyncHttpClient = new DefaultAsyncHttpClient() 11 | arm.ArmUtils.using(asyncHttpClient) { 12 | import java.nio.charset.Charset 13 | import monix.eval.Task 14 | val result6T = Task.create[String]( (_, callback) => { 15 | val lf = asyncHttpClient.prepareGet("https://httpbin.org/get").execute() 16 | val javaFuture = lf.toCompletableFuture 17 | 18 | javaFuture.whenComplete { (response, exc) => { 19 | if (exc == null) { 20 | callback.onSuccess(response.getResponseBody(Charset.forName("UTF-8"))) 21 | } else callback.onError(exc) 22 | }} 23 | 24 | import monix.execution.Cancelable 25 | Cancelable.apply { () => 26 | javaFuture.cancel(true) 27 | } 28 | }) 29 | 30 | import monix.execution.Scheduler.Implicits.global 31 | val resultCF = result6T.runToFuture 32 | 33 | import scala.concurrent.Await 34 | import scala.concurrent.duration._ 35 | val result = Await.result(resultCF, 5.seconds) 36 | println(result) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /monix/src/main/scala/ObservableMain.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by denis on 9/1/16. 3 | */ 4 | object ObservableMain { 5 | 6 | def main(args: Array[String]): Unit = { 7 | 8 | import monix.reactive.Observable 9 | val linesO = Observable.defer { 10 | import java.io.{BufferedReader, FileReader} 11 | val br = new BufferedReader(new FileReader("license.txt")) 12 | Observable.fromLinesReaderUnsafe(br) 13 | } 14 | 15 | printStatistics(linesO) 16 | printStatistics(linesO) 17 | 18 | def printStatistics(linesO: Observable[String]): Unit = { 19 | val wordsO = linesO.flatMap { line => 20 | val arr = line.split("\\W").map(_.toLowerCase) 21 | .map(_.trim).filter(!_.isEmpty) 22 | Observable.fromIterable(arr.toIterable) 23 | } 24 | 25 | val rawResultO = wordsO.foldLeft(Map.empty[String, Int]) { (acc, next) => 26 | acc.get(next) match { 27 | case None => acc + (next -> 1) 28 | case Some(num) => acc + (next -> (1 + num)) 29 | } 30 | } 31 | 32 | import monix.reactive.Consumer 33 | val finalResultT = rawResultO.map { map => 34 | map.toList.sortWith( _._2 > _._2).take(5).map(_._1) 35 | }.consumeWith(Consumer.head) 36 | 37 | import monix.execution.Scheduler.Implicits.global 38 | val resultCF = finalResultT.runToFuture 39 | 40 | import scala.concurrent.Await 41 | import scala.concurrent.duration._ 42 | val result = Await.result(resultCF, 30.seconds) 43 | println(result) 44 | // List(the, or, of, and, to) 45 | } 46 | 47 | } 48 | 49 | import cats.kernel.Monoid 50 | import monix.reactive.Observable 51 | def alternativeMonoid(wordsO: Observable[String]): Unit = { 52 | import cats.instances.int.catsKernelStdGroupForInt 53 | import cats.instances.map.catsKernelStdMonoidForMap 54 | 55 | val listT = wordsO.map(word => Map(word -> 1)).toListL 56 | val totals = listT.map { data => 57 | Monoid[Map[String, Int]].combineAll(data) 58 | } 59 | // totalsT: Task[Map[String, Int]] 60 | 61 | val finalResultT = totals.map { data => 62 | data.toList.sortWith( _._2 > _._2).take(5).map(_._1) 63 | } 64 | 65 | import monix.execution.Scheduler.Implicits.global 66 | import scala.concurrent.Await 67 | import scala.concurrent.duration._ 68 | val result = Await.result(finalResultT.runToFuture, 30.seconds) 69 | println(result) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /monocle/src/main/scala/MonocleMain.scala: -------------------------------------------------------------------------------- 1 | import java.util.UUID 2 | 3 | import monocle.{Iso, Lens, Optional, Prism} 4 | import spire.math.Rational 5 | 6 | /** 7 | * Created by denis on 8/5/16. 8 | */ 9 | 10 | object MonocleMain { 11 | 12 | def main(args: Array[String]) { 13 | 14 | // Lens 15 | { 16 | case class Person(firstName: String, lastName: String, age: Int, contacts: ContactInfo) 17 | case class ContactInfo(email: String, phone: String) 18 | 19 | { 20 | case class LensM[S, A](get: S => A)(set: A => S => S) 21 | val ageLens = LensM[Person, Int](_.age)(a => person => person.copy(age = a)) 22 | } 23 | 24 | // Getter 25 | val ageLens = Lens[Person, Int](_.age)(a => person => person.copy(age = a)) 26 | 27 | val person = Person("Joe", "Black", 42, ContactInfo("joe@example.com", "5551234")) 28 | println(s"Age: ${ageLens.get(person)}") 29 | 30 | val updatedAge = person.copy(age = 43) 31 | println(s"Age: ${updatedAge.age}") 32 | 33 | // Setter 34 | val emailLens = Lens[Person, String](_.contacts.email)(a => person => person.copy(contacts = person.contacts.copy(email = a))) 35 | val updated = emailLens.set("joe.black@example.com")(person) 36 | println(s"Email: ${emailLens.get(updated)}") 37 | 38 | // Macros 39 | import monocle.macros.GenLens 40 | val phoneLens = GenLens[Person](_.contacts.phone) 41 | val updatedPerson2 = phoneLens.set("5554321")(updated) 42 | println(s"Phone: ${updatedPerson2.contacts.phone}") 43 | 44 | // Lens composition 45 | val contactLens = GenLens[Person](_.contacts) 46 | val phoneContactLens = GenLens[ContactInfo](_.phone) 47 | val composedLens = contactLens.composeLens(phoneContactLens) 48 | println(s"Phone: ${composedLens.get(updatedPerson2)}") 49 | 50 | // Simple Iso demonstration 51 | val caseIso = Iso[String, String](_.toLowerCase)(_.toUpperCase) 52 | println(caseIso.get("MoNoClE")) 53 | println(caseIso.reverseGet("MoNoClE")) 54 | } 55 | 56 | // Iso 57 | { 58 | // Celsius to Fahrenheit 59 | import spire.syntax.literals._ 60 | val temp1 = r"232.112" 61 | val temp2 = r"222.874" 62 | val result = temp1 + temp2 63 | 64 | case class CelsiusD(value: Double) 65 | case class FahrenheitD(value: Double) 66 | 67 | case class Celsius(value: Rational) 68 | case class Fahrenheit(value: Rational) 69 | 70 | def cel2fahr(celsius: Celsius): Fahrenheit = Fahrenheit(celsius.value * r"9/5" + 32) 71 | def fahr2cel(fahrenheit: Fahrenheit): Celsius = Celsius((fahrenheit.value - 32) * r"5/9") 72 | 73 | println(cel2fahr(Celsius(20))) // prints Fahrenheit(68) 74 | println(fahr2cel(Fahrenheit(68))) // prints Celsius(20) 75 | 76 | val cel2FahrIso = Iso[Celsius, Fahrenheit](cel2fahr)(fahr2cel) 77 | 78 | println(cel2FahrIso.get(Celsius(20))) // prints Fahrenheit(68) 79 | println(cel2FahrIso.reverseGet(Fahrenheit(68))) // prints Celsius(20) 80 | 81 | val fahBoilingPoint=cel2FahrIso.get(Celsius(100)) 82 | println (cel2FahrIso.get(cel2FahrIso.reverseGet(fahBoilingPoint)) == fahBoilingPoint) 83 | 84 | case class Kelvin(value: Rational) 85 | def cel2kel(celsius: Celsius): Kelvin = Kelvin(celsius.value + r"273.15") 86 | def kel2cel(kelvin: Kelvin): Celsius = Celsius(kelvin.value - r"273.15") 87 | val cel2KelIso = Iso[Celsius, Kelvin](cel2kel)(kel2cel) 88 | 89 | // Getting Kelvin to Fahrenheit for free 90 | val kel2celIso = cel2KelIso.reverse 91 | val kel2FahrIso = kel2celIso.composeIso(cel2FahrIso) 92 | println(kel2FahrIso.get(Kelvin(r"273.15"))) // prints Fahrenheit(32) 93 | } 94 | 95 | 96 | // Prism 97 | { 98 | sealed trait ConfigValue 99 | case class IntValue(value: Int) extends ConfigValue 100 | case class StringValue(value: String) extends ConfigValue 101 | 102 | val portNumber: ConfigValue = IntValue(80) 103 | def offsetPort(port: Int): Int = port + 8000 104 | 105 | { 106 | val intConfP = Prism[ConfigValue, Int] { 107 | case IntValue(int) => Some(int) 108 | case _ => None 109 | }(IntValue.apply) 110 | val updatedPort = intConfP.modify(offsetPort)(portNumber) 111 | println(updatedPort) 112 | } 113 | } 114 | 115 | // Optional 116 | { 117 | case class User(name: String, age: Int, id: Option[UUID]) 118 | 119 | val idO = Optional[User, UUID](_.id)(id => user => user.copy(id = Some(id))) 120 | // idO: Optional[User, UUID] 121 | val register = idO.set(UUID.randomUUID()) 122 | // register: (User) => User 123 | 124 | val unregistered = User("Joe", 42, None) 125 | val registeredUser = register(unregistered) 126 | println(registeredUser) 127 | // prints User(Joe,42,Some(24c56ff4-4f03-4b0d-b6b6-33e38460d884)) 128 | 129 | val scores = Map("Joe" -> 10, "Alice" -> 12) 130 | def scoreO(name: String) = Optional[Map[String, Int], Int]( 131 | _.get(name))( score => map => map.updated(name, score) ) 132 | 133 | val joeScoreO = scoreO("Joe") 134 | // joeScoreO: Optional[Map[String, Int], Int] 135 | val joeScore = joeScoreO.getOption(scores) 136 | println(joeScore) 137 | // prints Some(10) 138 | 139 | val johnScoreO = scoreO("John") 140 | val johnScore = johnScoreO.getOption(scores) 141 | println(johnScore) 142 | // prints None 143 | 144 | val updatedScores = johnScoreO.set(15)(scores) 145 | println(updatedScores) 146 | // prints Map(Joe -> 10, Alice -> 12, John -> 15) 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.4.7 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.lyranthe.sbt" % "partial-unification" % "1.1.2") -------------------------------------------------------------------------------- /scalaz/src/main/scala/ScalazActorMain.scala: -------------------------------------------------------------------------------- 1 | import akka.Message 2 | 3 | import scalaz.concurrent.{Actor, Strategy} 4 | import scalaz.concurrent.Actor._ 5 | 6 | 7 | class MyActor { 8 | private var counter = 0 9 | def handler(message: Message): Unit = { 10 | counter += 1 11 | println(s"#$counter: ${message.text}") 12 | } 13 | } 14 | object MyActor { 15 | def create: Actor[Message] = Actor.actor(new MyActor().handler) 16 | } 17 | 18 | 19 | object ScalazActorMain { 20 | 21 | def main (args: Array[String]): Unit = { 22 | /* 23 | val actors = Array(MyActor.create, MyActor.create) 24 | 25 | 1.to(20).foreach { i => 26 | val actor = if (i % 2 == 0) actors(0) else actors(1) 27 | actor ! Message(s"Message $i") 28 | Thread.sleep(100) 29 | }*/ 30 | 31 | val actor = Actor.actor( (message: Message) => { 32 | println(s"Received message: ${message.text}") 33 | }, error => { 34 | System.err.println(error.getMessage) 35 | })(Strategy.Sequential) 36 | 37 | actor ! Message("HOHO") 38 | 39 | // Thread.sleep(5000) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /scalaz/src/main/scala/ScalazDisjunction.scala: -------------------------------------------------------------------------------- 1 | import scalaz.\/ 2 | 3 | /** 4 | * Created by denis on 9/4/16. 5 | */ 6 | class GenerationException(number: Long, message: String) 7 | extends Exception(message) 8 | 9 | object ScalazDisjunction { 10 | 11 | def queryNextNumberE: Exception \/ Long = { 12 | val source = Math.round(Math.random * 100) 13 | if (source <= 60) \/.right(source) 14 | else \/.left(new Exception("The generated number is too big!")) 15 | } 16 | 17 | def queryNextNumberT: Throwable \/ Long = \/.fromTryCatchNonFatal { 18 | val source = Math.round(Math.random * 100) 19 | if (source <= 60) source 20 | else throw new Exception("The generated number is too big!") 21 | } 22 | 23 | def queryNextNumberGE: GenerationException \/ Long = \/.fromTryCatchThrowable[Long, GenerationException] { 24 | val source = Math.round(Math.random * 100) 25 | if (source <= 90) source 26 | else throw new GenerationException(source, "The generated number is too big!") 27 | } 28 | 29 | def main(args: Array[String]): Unit = { 30 | 31 | val lst = List(queryNextNumberGE, queryNextNumberGE, queryNextNumberGE) 32 | 33 | import scalaz._ 34 | import Scalaz._ 35 | val lstD = lst.sequence 36 | println(lstD) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /scalaz/src/main/scala/ScalazMain.scala: -------------------------------------------------------------------------------- 1 | import java.nio.charset.Charset 2 | import java.util.concurrent.Executors 3 | import org.asynchttpclient.DefaultAsyncHttpClient 4 | import scala.concurrent.Future 5 | import scalaz.{-\/, \/, \/-} 6 | import scalaz.concurrent.Task 7 | 8 | /** 9 | * Created by denis on 7/24/16. 10 | */ 11 | object ScalazMain { 12 | 13 | def main(args: Array[String]): Unit = { 14 | 15 | def performAction(num: Int): Unit = println(s"Task #$num is executing in ${Thread.currentThread().getName}") 16 | 17 | import scala.concurrent.ExecutionContext.Implicits.global 18 | val result1F = Future { 19 | performAction(0) 20 | } 21 | 22 | val result2F = Future.successful { 23 | performAction(1) 24 | } 25 | 26 | // Executes immediately in the main thread 27 | val result2T = Task.now { 28 | performAction(2) 29 | } 30 | 31 | // Schedules an execution in a default worker thread 32 | // = Executors.newFixedThreadPool(Math.max(4, Runtime.getRuntime.availableProcessors), DefaultDaemonThreadFactory) 33 | val result3T = Task { 34 | performAction(3) 35 | } 36 | 37 | // Lifts a code block to a Task without scheduling an execution 38 | val result4T = Task.delay { 39 | performAction(4) 40 | } 41 | 42 | result3T.unsafePerformAsync(_ => ()) 43 | 44 | implicit val executorService = Executors.newSingleThreadExecutor() 45 | val result5T = Task { 46 | performAction(5) 47 | } 48 | result3T.unsafePerformSync 49 | 50 | val asyncHttpClient = new DefaultAsyncHttpClient() 51 | arm.ArmUtils.using(asyncHttpClient) { 52 | val result6T = Task.async[String](handler => { 53 | asyncHttpClient.prepareGet("https://httpbin.org/get").execute(). 54 | toCompletableFuture.whenComplete { (response, exc) => { 55 | if (exc == null) { 56 | handler(\/.right(response.getResponseBody(Charset.forName("UTF-8")))) 57 | } else handler(-\/(exc)) 58 | }} 59 | }) 60 | val responseString = result6T.unsafePerformSync 61 | println(responseString) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /scalaz/src/main/scala/akka/AkkaActorMain.scala: -------------------------------------------------------------------------------- 1 | package akka 2 | 3 | import akka.actor.Actor 4 | import akka.actor.Actor.Receive 5 | 6 | 7 | 8 | 9 | case class Message(text: String) 10 | 11 | class MyActor extends Actor { 12 | private var counter = 0 13 | override def receive = { 14 | case message: Message => 15 | counter += 1 16 | println(s"#$counter: ${message.text}") 17 | } 18 | } 19 | 20 | object AkkaActorMain { 21 | 22 | def main(args: Array[String]) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /shapeless/src/main/scala/CodecDerivation.scala: -------------------------------------------------------------------------------- 1 | import shapeless.ops.hlist.{Mapper, ToTraversable} 2 | import shapeless._ 3 | import shapeless.ops.record.{Keys, Values} 4 | 5 | /** 6 | * Created by denis on 8/26/16. 7 | */ 8 | object CodecDerivation { 9 | 10 | object JsonEncoder { 11 | 12 | object quoteStrings extends Poly1 { 13 | implicit def str2str = at[String](str => s""""$str"""") 14 | implicit def default[A] = at[A](identity) 15 | } 16 | 17 | def doEncode[A, T <: HList, K <: HList, V <: HList, R <: HList](a: A)( 18 | implicit gen: LabelledGeneric.Aux[A, T], 19 | keyExtractor: Keys.Aux[T, K], 20 | valueExtractor: Values.Aux[T, V], 21 | valueMapper: Mapper.Aux[quoteStrings.type, V, R], 22 | toTraversableValues: ToTraversable.Aux[R, List, Any], 23 | toTraversableKeys: ToTraversable.Aux[K, List, Symbol] 24 | ): String = { 25 | val repr = gen.to(a) 26 | val keys = keyExtractor.apply 27 | val values = valueExtractor.apply(repr) 28 | 29 | val quoted = values.map(quoteStrings) 30 | val zipped = keys.toList.zip(quoted.toList) 31 | 32 | val elements = zipped.map { case (label, value) => 33 | "\"" + label.name + "\"" + ":" + value.toString 34 | } 35 | val json = elements.mkString("{", ",", "}") 36 | json 37 | } 38 | } 39 | 40 | def main(args: Array[String]) { 41 | 42 | case class Person(firstName: String, lastName: String, age: Int) 43 | val person = Person("Joe", "Black", 42) 44 | 45 | val json = JsonEncoder.doEncode(person) 46 | println(json) 47 | // prints {"firstName":"Joe","lastName":"Black","age":42} 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /shapeless/src/main/scala/HListMain.scala: -------------------------------------------------------------------------------- 1 | import shapeless.ops.hlist.Mapper 2 | 3 | package base { 4 | sealed trait Base extends Product with Serializable 5 | case class User(firstName: String, lastName: String) extends Base 6 | case class Credentials(username: String, password: String) extends Base 7 | } 8 | 9 | 10 | /** 11 | * Created by denis on 8/18/16. 12 | */ 13 | object HListMain { 14 | 15 | def main(args: Array[String]) { 16 | 17 | { 18 | import shapeless._ 19 | val hlist = 1 :: "string" :: HNil 20 | println(hlist) 21 | 22 | object upperPoly extends Poly1 { 23 | implicit def string = at[String](_.toUpperCase) 24 | implicit def default[A] = at[A](identity) 25 | } 26 | 27 | val updated = hlist.map(upperPoly) 28 | println(updated) 29 | 30 | def makeUpperCase[T <: HList](list: T)(implicit mapper: 31 | Mapper.Aux[upperPoly.type, T, T]): mapper.Out = list.map(upperPoly) 32 | 33 | val list = updated.unify 34 | println(list) 35 | } 36 | 37 | { 38 | import shapeless.syntax.std.tuple._ 39 | 40 | val pair = ("str", 3) 41 | val it = pair.productIterator 42 | val added = it ++ Iterator(3.14) 43 | println(added.toList) 44 | 45 | val addedS = pair :+ 3.14 46 | println(addedS.reverse.head) 47 | 48 | val tupleHList = addedS.productElements 49 | println(tupleHList) 50 | 51 | println(tupleHList.tupled) 52 | } 53 | 54 | { 55 | import shapeless._ 56 | import _root_.base._ 57 | 58 | val user = User("Joe", "Black") 59 | val credentials = Credentials("joe", "password123") 60 | val userInfo = user :: credentials :: HNil 61 | 62 | val infoList = userInfo.toList 63 | println(infoList) 64 | } 65 | 66 | { 67 | val stringE: Either[Int, String] = Right("Joe") 68 | val intE: Either[Int, String] = Left(42) 69 | } 70 | 71 | { 72 | import shapeless._ 73 | 74 | type ESI = Exception :+: String :+: Int :+: CNil 75 | 76 | val excESI = Coproduct[ESI](new Exception) 77 | val strESI = Coproduct[ESI]("Joe Black") 78 | val intESI = Coproduct[ESI](42) 79 | 80 | println(strESI) 81 | // prints Inr(Inl(Joe Black)) 82 | println(strESI.select[String]) 83 | // prints Some(Joe Black) 84 | 85 | object incPoly extends Poly1 { 86 | implicit def string = at[Int](_ + 1) 87 | implicit def default[A] = at[A](identity) 88 | } 89 | 90 | val updIntESI = intESI.map(incPoly) 91 | println(updIntESI.select[Int]) 92 | } 93 | 94 | { 95 | import shapeless._ 96 | 97 | case class Person(firstName: String, id: Long, age: Int) 98 | val person = Person("Joe", 1234567L, 42) 99 | val personGen = Generic[Person] 100 | 101 | val repr = personGen.to(person) 102 | println(repr) 103 | // prints Joe :: 1234567 :: 42 :: HNil 104 | 105 | val samePerson = personGen.from(repr) 106 | println(samePerson) 107 | // prints Person(Joe,1234567,42) 108 | } 109 | 110 | { 111 | import shapeless._ 112 | 113 | sealed trait A 114 | case class B(value: String) extends A 115 | case class C(value: Int) extends A 116 | 117 | val aGen = Generic[A] 118 | 119 | val b = B("string") 120 | println(aGen.to(b)) 121 | // Inl(B(string))russian empire 122 | 123 | val c = C(42) 124 | println(aGen.to(c)) 125 | // Inr(Inl(C(42))) 126 | } 127 | 128 | { 129 | 130 | 131 | import shapeless._ 132 | import shapeless.syntax.std.function._ 133 | import shapeless.ops.function._ 134 | 135 | def applyProduct[P <: Product, F, L <: HList, R](p: P)(f: F) 136 | (implicit gen: Generic.Aux[P, L], fp: FnToProduct.Aux[F, L => R]) = 137 | f.toProduct(gen.to(p)) 138 | 139 | case class UserScore(name: String, score: Int) 140 | 141 | val joeScore = UserScore("joe", 10) 142 | val lisaScore = UserScore("lisa", 12) 143 | val total = applyProduct(joeScore, lisaScore)( 144 | (_: UserScore).score + (_: UserScore).score) 145 | println(total) 146 | // prints 22 147 | 148 | val johnScore = UserScore("john", 23) 149 | val total2 = applyProduct(joeScore, lisaScore, johnScore)( 150 | (_: UserScore).score + (_: UserScore).score + (_: UserScore).score) 151 | println(total2) 152 | // prints 45 153 | } 154 | 155 | 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /shapeless/src/main/scala/ShapelessMain.scala: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by denis on 8/17/16. 3 | */ 4 | package byo { 5 | 6 | package list { 7 | // List 8 | sealed trait MyList[+T] { 9 | def ::[TT >: T](el: TT): MyList[TT] = MyCons(el, this) 10 | } 11 | 12 | case object MyNil extends MyList[Nothing] 13 | 14 | case class MyCons[+T](head: T, tail: MyList[T]) extends MyList[T] 15 | } 16 | 17 | package hlist { 18 | // HList 19 | sealed trait HList 20 | 21 | case object HNil extends HList { 22 | def ::[T](el: T): HCons[T, HNil.type] = HCons(el, this) 23 | } 24 | 25 | case class HCons[+H, +T <: HList](head: H, tail: T) extends HList { 26 | def ::[F](el: F): HCons[F, HCons[H, T]] = HCons(el, this) 27 | } 28 | } 29 | 30 | package operations { 31 | trait Operation[T] { 32 | type Result 33 | def apply(t: T): Result 34 | } 35 | 36 | object Operation { 37 | type Aux[T0, Result0] = Operation[T0] { type Result = Result0 } 38 | } 39 | 40 | trait Op[T, Result] { 41 | def apply(t: T): Result 42 | } 43 | } 44 | } 45 | 46 | object ShapelessMain { 47 | 48 | def main(args: Array[String]) { 49 | 50 | { 51 | import _root_.byo.list._ 52 | 53 | val ints = 3 :: 2 :: MyNil 54 | println(ints) 55 | 56 | val list = 3 :: "str" :: MyNil 57 | println(list) 58 | } 59 | 60 | { 61 | import _root_.byo.hlist._ 62 | 63 | val hList = 3 :: "str" :: HNil 64 | println(hList) 65 | } 66 | 67 | { 68 | import _root_.byo.operations._ 69 | 70 | implicit val strLen = new Operation[String] { 71 | type Result = Int 72 | def apply(t: String): Result = t.length 73 | } 74 | println(strLen("hello")) 75 | 76 | implicit val intInc = new Operation[Int] { 77 | type Result = Int 78 | def apply(t: Int): Result = t + 1 79 | } 80 | println(intInc(5)) 81 | 82 | def applyOps[T, R](t: T)(implicit op: Operation.Aux[T, R], op2: Operation[R]): 83 | op2.Result = op2.apply(op.apply(t)) 84 | println(applyOps("hello")) 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /typeclasses/src/main/scala/TypeClasses.scala: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | // Type class itself 5 | trait InfoPrinter[T] { 6 | def toInfo(value: T): String 7 | } 8 | 9 | // Default instances 10 | object DefaultInfoPrinters { 11 | implicit val stringPrinter = new macros.InfoPrinter[String] { 12 | override def toInfo(value: String): String = s"[String] $value" 13 | } 14 | implicit val intPrinter = new macros.InfoPrinter[Int] { 15 | override def toInfo(value: Int): String = s"[Int] $value" 16 | } 17 | } 18 | 19 | // Singleton approach 20 | object PrintInfo { 21 | def printInfo[A](value: A)(implicit printer: macros.InfoPrinter[A]): Unit = { 22 | println(printer.toInfo(value)) 23 | } 24 | } 25 | 26 | // Syntax approach (implicit conversions) 27 | object PrintInfoSyntax { 28 | implicit class PrintInfoOps[T](value: T) { 29 | def printInfo()(implicit printer: macros.InfoPrinter[T]): Unit = { 30 | println(printer.toInfo(value)) 31 | } 32 | } 33 | } 34 | 35 | // Custom user defined class 36 | case class User(name: String, age: Int) 37 | 38 | // User defined type class instance 39 | object User { 40 | implicit val userPrinter = new macros.InfoPrinter[User] { 41 | override def toInfo(value: User): String = s"[User] (${value.name}, ${value.age})" 42 | } 43 | } 44 | 45 | 46 | 47 | /* 48 | Everything that has the `toinfo` method is a type class, so type classes categorize things 49 | that have some commonality. For example, in Java we had `compare` method and it actually 50 | produced type class Comparable. The problem with Java is that it doesn't have the `implicit` keyword 51 | and therefore there is no reason to call it a pattern. 52 | */ 53 | object Main { 54 | def main(args: Array[String]) = { 55 | 56 | val number = 42 57 | import DefaultInfoPrinters._ 58 | // PrintInfo.printInfo(number) 59 | 60 | import PrintInfoSyntax._ 61 | number.printInfo() 62 | 63 | User("Joe", 42).printInfo() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /typeclasses/src/main/scala/macros/SimulacrumTypeClasses.scala: -------------------------------------------------------------------------------- 1 | package macros 2 | 3 | /** 4 | * Created by denis on 7/29/16. 5 | */ 6 | 7 | import simulacrum._ 8 | 9 | @typeclass trait InfoPrinter[T] { 10 | def toInfo(value: T): String 11 | } 12 | 13 | // Custom user defined class 14 | case class User(name: String, age: Int) 15 | 16 | // User defined type class instance 17 | object User { 18 | implicit val userPrinter = new InfoPrinter[User] { 19 | override def toInfo(value: User): String = s"[User] (${value.name}, ${value.age})" 20 | } 21 | } 22 | 23 | object SimulacrumTypeClasses { 24 | 25 | def main(args: Array[String]) { 26 | 27 | import InfoPrinter.ops._ 28 | val user = User("Joe", 42) 29 | println(user.toInfo) 30 | 31 | } 32 | } 33 | --------------------------------------------------------------------------------