├── .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 |
--------------------------------------------------------------------------------