├── Chapter11 └── jvm │ ├── sample.txt │ ├── project │ └── build.properties │ ├── goods.csv │ ├── goods.json │ ├── log.txt │ ├── build.sbt │ └── src │ └── main │ └── scala │ └── jvm │ └── Deadlock.scala ├── Chapter01 ├── jvm │ ├── README.md │ ├── project │ │ └── build.properties │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── scala │ │ │ └── jvm │ │ │ │ └── MainScala.scala │ │ │ └── java │ │ │ └── jvm │ │ │ └── MainJava.java │ └── build.sbt └── cpp │ ├── run.sh │ ├── a.out │ ├── loop.cpp │ ├── nested-for-loop.cpp │ └── nested-loop.cpp ├── Chapter02 └── jvm │ ├── README.md │ ├── project │ └── build.properties │ ├── src │ └── main │ │ ├── java │ │ └── jvm │ │ │ └── soda │ │ │ ├── SodaCan.java │ │ │ ├── SodaImperative.java │ │ │ └── ImperativeSodaMachine.java │ │ └── scala │ │ └── jvm │ │ ├── higherorder │ │ └── While.scala │ │ ├── soda │ │ └── SodaFunctional.scala │ │ └── lambda │ │ └── Lambda.scala │ ├── .gitignore │ └── build.sbt ├── Chapter13 ├── README.md ├── project │ └── build.properties ├── .gitignore ├── src │ ├── test │ │ └── scala │ │ │ └── crawler │ │ │ └── MainSpec.scala │ └── main │ │ └── scala │ │ └── crawler │ │ ├── Main.scala │ │ └── ActorBased.scala └── build.sbt ├── Chapter03 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ ├── scala │ │ └── jvm │ │ │ ├── mapping │ │ │ ├── MappingInfix.scala │ │ │ └── MappingFunctional.scala │ │ │ └── effect │ │ │ ├── OptionDemo.scala │ │ │ └── TryDemo.scala │ │ └── java │ │ └── jvm │ │ └── mapping │ │ └── MappingImperative.java │ └── build.sbt ├── Chapter04 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ └── scala │ │ └── jvm │ │ ├── DivisionByZero.scala │ │ ├── DatabaseNull.scala │ │ ├── SodaMachineTry.scala │ │ ├── BlackBox.scala │ │ ├── DatabaseLogged.scala │ │ ├── GameActors.scala │ │ └── GameActorsImmutable.scala │ └── build.sbt ├── Chapter05 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ └── scala │ │ └── jvm │ │ ├── Division.scala │ │ ├── SignatureDemos.scala │ │ └── FunctionalAbstractions.scala │ └── build.sbt ├── Chapter06 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ └── scala │ │ └── jvm │ │ ├── DivisionEither.scala │ │ ├── Bank.scala │ │ └── Weather.scala │ └── build.sbt ├── Chapter07 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ └── scala │ │ └── jvm │ │ ├── FilterString.scala │ │ ├── WebAPI.scala │ │ └── Logging.scala │ └── build.sbt ├── Chapter08 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ └── scala │ │ └── jvm │ │ ├── Tailrec.scala │ │ ├── WriterFunctor.scala │ │ ├── WriterExample.scala │ │ ├── TailRecM.scala │ │ ├── MonoidEither.scala │ │ ├── AdditionMonadic.scala │ │ ├── TraverseExample.scala │ │ └── IndependentComputations.scala │ └── build.sbt ├── Chapter09 ├── jvm │ ├── project │ │ └── build.properties │ ├── goods.csv │ ├── src │ │ └── main │ │ │ └── scala │ │ │ └── jvm │ │ │ ├── server │ │ │ ├── Model.scala │ │ │ ├── db │ │ │ │ ├── infrastructure.scala │ │ │ │ ├── order.scala │ │ │ │ ├── good.scala │ │ │ │ └── Customer.scala │ │ │ ├── CirceImplicits.scala │ │ │ └── Server.scala │ │ │ ├── Resources.scala │ │ │ └── Concurrency.scala │ └── build.sbt └── bash │ └── client.sh ├── Chapter10 └── jvm │ ├── project │ └── build.properties │ ├── sales.csv │ ├── build.sbt │ └── src │ └── main │ └── scala │ └── jvm │ ├── TaglessFinal.scala │ ├── HttpServer.scala │ └── HListSum.scala ├── Chapter12 └── jvm │ ├── project │ └── build.properties │ ├── src │ └── main │ │ ├── resources │ │ └── application.conf │ │ └── scala │ │ └── jvm │ │ └── HelloWorld.scala │ └── build.sbt ├── start.sh ├── postgres ├── Dockerfile └── schema.sql ├── compose.sh ├── Dockerfile ├── .gitignore ├── docker-compose.yml ├── LICENSE └── README.md /Chapter11/jvm/sample.txt: -------------------------------------------------------------------------------- 1 | Hello World -------------------------------------------------------------------------------- /Chapter01/jvm/README.md: -------------------------------------------------------------------------------- 1 | Simple Sbt project with Scala. 2 | -------------------------------------------------------------------------------- /Chapter02/jvm/README.md: -------------------------------------------------------------------------------- 1 | Simple Sbt project with Scala. 2 | -------------------------------------------------------------------------------- /Chapter13/README.md: -------------------------------------------------------------------------------- 1 | Simple Sbt project with Scala. 2 | -------------------------------------------------------------------------------- /Chapter13/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /Chapter01/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter02/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter03/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter04/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter05/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter06/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter07/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter08/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter09/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.6 2 | -------------------------------------------------------------------------------- /Chapter10/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter11/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.1 2 | -------------------------------------------------------------------------------- /Chapter12/jvm/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.6 2 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker exec -ti mastering_backend bash 3 | -------------------------------------------------------------------------------- /Chapter01/cpp/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm a.out; g++ $1; ./a.out 4 | -------------------------------------------------------------------------------- /Chapter12/jvm/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka.log-dead-letters=0 2 | -------------------------------------------------------------------------------- /Chapter10/jvm/sales.csv: -------------------------------------------------------------------------------- 1 | Name,Price 2 | Bread,2 3 | Butter,4 4 | Cabbage,3 5 | Water,2 6 | -------------------------------------------------------------------------------- /postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:latest 2 | ADD ./*.sql /docker-entrypoint-initdb.d/ 3 | -------------------------------------------------------------------------------- /compose.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker-compose down 3 | docker-compose build 4 | docker-compose up 5 | -------------------------------------------------------------------------------- /Chapter09/jvm/goods.csv: -------------------------------------------------------------------------------- 1 | Name,Price 2 | Samsung Galaxy S5,300 3 | Apple TV,1200 4 | MacBookPro,1300 5 | -------------------------------------------------------------------------------- /Chapter01/cpp/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Functional-Programming/HEAD/Chapter01/cpp/a.out -------------------------------------------------------------------------------- /Chapter11/jvm/goods.csv: -------------------------------------------------------------------------------- 1 | Name,Price 2 | TV Set,100 3 | iPhone 8,300 4 | Samsung Galaxy S5,300 5 | MacBook Pro,2500 6 | MacBook Air,1500 7 | -------------------------------------------------------------------------------- /Chapter07/jvm/src/main/scala/jvm/FilterString.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object FilterString extends App { 4 | println("Foo".filter(_ != 'o')) // "F" 5 | } 6 | -------------------------------------------------------------------------------- /Chapter02/jvm/src/main/java/jvm/soda/SodaCan.java: -------------------------------------------------------------------------------- 1 | package jvm.soda; 2 | 3 | public class SodaCan { 4 | public void drink() { 5 | System.out.println("You have drunk a can of soda."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Chapter03/jvm/src/main/scala/jvm/mapping/MappingInfix.scala: -------------------------------------------------------------------------------- 1 | package jvm.mapping 2 | 3 | object MappingInfix extends App{ 4 | val numbers = List(1, 2, 3) 5 | val result = numbers map (n => n * n) 6 | println(result) 7 | } 8 | -------------------------------------------------------------------------------- /Chapter11/jvm/goods.json: -------------------------------------------------------------------------------- 1 | {"Name": "TV Set", "Price": 100} 2 | {"Name": "iPhone 8", "Price": 300} 3 | {"Name": "Samsung Galaxy S5", "Price": 300} 4 | {"Name": "MacBook Pro", "Price": 2500} 5 | {"Name": "MacBook Air", "Price": 1500} 6 | -------------------------------------------------------------------------------- /Chapter01/cpp/loop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // main() is where program execution begins. 5 | int main() { 6 | int x = 0; 7 | loop_start: 8 | x++; 9 | cout << x << "\n"; 10 | if (x < 10) goto loop_start; 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /Chapter03/jvm/src/main/scala/jvm/mapping/MappingFunctional.scala: -------------------------------------------------------------------------------- 1 | package jvm.mapping 2 | 3 | object MappingFunctional { 4 | def main(args: Array[String]): Unit = { 5 | val numbers = List(1, 2, 3) 6 | val result = numbers.map(n => n * n) 7 | println(result) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter13/.gitignore: -------------------------------------------------------------------------------- 1 | ###Scala### 2 | 3 | *.class 4 | *.log 5 | 6 | # sbt specific 7 | .cache/ 8 | .history/ 9 | .lib/ 10 | dist/* 11 | target/ 12 | lib_managed/ 13 | src_managed/ 14 | project/boot/ 15 | project/plugins/project/ 16 | 17 | # Scala-IDE specific 18 | .scala_dependencies 19 | .worksheet 20 | 21 | # Ensime 22 | /.ensime* -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM hseeberger/scala-sbt 2 | 3 | RUN mkdir -p /root/.sbt/1.0/plugins 4 | RUN echo "\ 5 | addSbtPlugin(\"io.get-coursier\" % \"sbt-coursier\" % \"1.0.0-RC12-1\")\n\ 6 | addSbtPlugin(\"io.spray\" % \"sbt-revolver\" % \"0.9.0\" )\n\ 7 | " > /root/.sbt/1.0/plugins/plugins.sbt 8 | 9 | WORKDIR /root/examples 10 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/DivisionByZero.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object DivisionByZero extends App { 4 | def division(n1: Double, n2: Double): Double = 5 | if (n2 == 0) throw new RuntimeException("Division by zero!") 6 | else n1 / n2 7 | 8 | division(1, 0) 9 | println("This line will never be executed") 10 | } 11 | -------------------------------------------------------------------------------- /Chapter13/src/test/scala/crawler/MainSpec.scala: -------------------------------------------------------------------------------- 1 | package crawler 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop.forAll 5 | 6 | object MainSpec extends Properties("MainSpec") { 7 | 8 | property("&& law") = forAll { x: Boolean => (x && false) == false } 9 | property("|| law") = forAll { x: Boolean => (x || true ) == true } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /Chapter06/jvm/src/main/scala/jvm/DivisionEither.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object Division extends App { 4 | 5 | def division(n1: Double, n2: Double): Either[String, Double] = 6 | if (n2 == 0) Left("Division by zero!") 7 | else Right(n1 / n2) 8 | 9 | println(division(1, 0)) // Left("Division by Zero") 10 | println(division(2, 2)) // Right(1.0) 11 | } 12 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/DatabaseNull.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object DatabaseNull extends App { 4 | case class User(name: String) 5 | 6 | def getUser(id: Int): User = 7 | if (Set(1, 2, 3).contains(id)) User(s"User-$id") 8 | else null 9 | 10 | 11 | println(getUser(1 ).name) // User-1 12 | println(getUser(10).name) // NullPointerException 13 | } 14 | -------------------------------------------------------------------------------- /Chapter01/jvm/.gitignore: -------------------------------------------------------------------------------- 1 | ###Scala### 2 | 3 | *.class 4 | *.log 5 | 6 | # sbt specific 7 | .cache/ 8 | .history/ 9 | .lib/ 10 | dist/* 11 | target/ 12 | lib_managed/ 13 | src_managed/ 14 | project/boot/ 15 | project/plugins/project/ 16 | 17 | # Scala-IDE specific 18 | .scala_dependencies 19 | .worksheet 20 | 21 | # Ensime 22 | /.ensime* 23 | 24 | # Project-specific data 25 | /data/ 26 | -------------------------------------------------------------------------------- /Chapter02/jvm/.gitignore: -------------------------------------------------------------------------------- 1 | ###Scala### 2 | 3 | *.class 4 | *.log 5 | 6 | # sbt specific 7 | .cache/ 8 | .history/ 9 | .lib/ 10 | dist/* 11 | target/ 12 | lib_managed/ 13 | src_managed/ 14 | project/boot/ 15 | project/plugins/project/ 16 | 17 | # Scala-IDE specific 18 | .scala_dependencies 19 | .worksheet 20 | 21 | # Ensime 22 | /.ensime* 23 | 24 | # Project-specific data 25 | /data/ 26 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/Model.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | 3 | case class Customer( 4 | id : Option[Int] = None 5 | , name: String) 6 | 7 | case class Good( 8 | id : Option[Int] = None 9 | , name : String 10 | , price: Double 11 | , stock: Int) 12 | 13 | case class Order( 14 | id : Option[Int] = None 15 | , customer : Int 16 | , good : Int) 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ###Scala### 2 | 3 | *.class 4 | *.log 5 | 6 | # sbt specific 7 | .cache/ 8 | .history/ 9 | .lib/ 10 | dist/* 11 | target/ 12 | lib_managed/ 13 | src_managed/ 14 | project/boot/ 15 | project/plugins/project/ 16 | 17 | # Scala-IDE specific 18 | .scala_dependencies 19 | .worksheet 20 | .vscode/* 21 | 22 | # Ensime 23 | /.ensime* 24 | 25 | # Project-specific 26 | /data/ 27 | /_volumes/ -------------------------------------------------------------------------------- /Chapter01/cpp/nested-for-loop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int main() { 5 | int rows = 3; 6 | int cols = 3; 7 | int matrix[3][3] = { 8 | { 1, 2, 3 }, 9 | { 4, 5, 6 }, 10 | { 7, 8, 9 } 11 | }; 12 | 13 | for (int r = 0; r < rows; r++) { 14 | for (int c = 0; c < cols; c++) cout << matrix[r][c] << " "; 15 | cout << "\n"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/SodaMachineTry.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.util.Try 4 | 5 | object SodaMachineTry extends App { 6 | class SodaCan 7 | 8 | var cans = 0 9 | def insertCoin(): Try[SodaCan] = Try { 10 | if (cans > 0) { cans -= 1; new SodaCan } 11 | else throw new RuntimeException("Out of soda cans!") 12 | } 13 | 14 | println(insertCoin()) 15 | } 16 | -------------------------------------------------------------------------------- /Chapter02/jvm/src/main/java/jvm/soda/SodaImperative.java: -------------------------------------------------------------------------------- 1 | package jvm.soda; 2 | 3 | 4 | public class SodaImperative { 5 | public static void main(String[] args) { 6 | ImperativeSodaMachine sm = new ImperativeSodaMachine(2); 7 | SodaCan can1 = sm.insertCoin(); 8 | SodaCan can2 = sm.insertCoin(); 9 | can2.drink(); 10 | 11 | SodaCan can3 = sm.insertCoin(); // Out of soda cans 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter01/jvm/src/main/scala/jvm/MainScala.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object MainScala { 4 | def main(args: Array[String]): Unit = { 5 | // Source collection 6 | val employees = List( 7 | "Ann" 8 | , "John" 9 | , "Amos" 10 | , "Jack") 11 | 12 | // Those employees with their names starting with 'A' 13 | val result = employees.filter { e => e(0) == 'A' } 14 | println(result) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/Tailrec.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object Tailrec extends App { 4 | def factorial(n: Int): Int = 5 | if (n <= 0) 1 6 | else n * factorial(n - 1) 7 | 8 | println(factorial(5)) // 120 9 | 10 | @annotation.tailrec 11 | def factorialTailrec(n: Int, accumulator: Int = 1): Int = 12 | if (n <= 0) accumulator 13 | else factorialTailrec(n - 1, n * accumulator) 14 | 15 | println(factorialTailrec(5)) // 120 16 | } 17 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/WriterFunctor.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats._, cats.implicits._ 4 | 5 | object WriterFunctor extends App { 6 | implicit val simpleWriterFunctor: Functor[SimpleWriter] = new Functor[SimpleWriter] { 7 | override def map[A, B](fa: SimpleWriter[A])(f: A => B): SimpleWriter[B] = 8 | fa.copy(value = f(fa.value)) 9 | } 10 | 11 | val x = SimpleWriter(Nil, 3) 12 | println(x.map(_ * 2)) // SimpleWriter(List(),6) 13 | } 14 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/db/infrastructure.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | package db 3 | 4 | import cats._, cats.implicits._, cats.effect._ 5 | import doobie._, doobie.implicits._ 6 | 7 | object infrastructure { 8 | implicit lazy val tr: Transactor[IO] = 9 | Transactor.fromDriverManager[IO]( 10 | "org.postgresql.Driver" 11 | , s"jdbc:postgresql://${sys.env("POSTGRES_HOST")}:${sys.env("POSTGRES_PORT")}/postgres" 12 | , "postgres", "") 13 | } 14 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/BlackBox.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object BlackBox extends App { 4 | val listMutable : Seq[Int] = collection.mutable.ListBuffer[Int](1, 2, 3) 5 | val listImmutable: Seq[Int] = List(1, 2, 3) 6 | 7 | def blackBox(x: Seq[Int]): Unit = ??? 8 | 9 | blackBox(listMutable) // Anything could happen to listMutable here, because it is mutable 10 | blackBox(listImmutable) // No matter what happens, listImmutable remains the same, because it is immutable 11 | } 12 | -------------------------------------------------------------------------------- /Chapter02/jvm/src/main/java/jvm/soda/ImperativeSodaMachine.java: -------------------------------------------------------------------------------- 1 | package jvm.soda; 2 | 3 | 4 | public class ImperativeSodaMachine { 5 | private int coins = 0; 6 | private int cans = 0; 7 | 8 | public ImperativeSodaMachine(int initialCans) { 9 | this.cans = initialCans; 10 | } 11 | 12 | public SodaCan insertCoin() { 13 | if (cans > 0) { 14 | cans--; 15 | coins++; 16 | return new SodaCan(); 17 | } 18 | else throw new RuntimeException("Out of soda cans!"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/DatabaseLogged.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object DatabaseLogged extends App { 4 | 5 | def getUserName(id: Int): String = { 6 | val name = s"User-$id" 7 | println(s"LOG: Requested user: $name") 8 | name 9 | } 10 | 11 | def getUserNamePure(id: Int): (List[String], String) = { 12 | val name = s"User-$id" 13 | val log = List(s"LOG: Requested user: $name") 14 | (log, name) 15 | } 16 | 17 | val u = getUserName(10) 18 | 19 | val u2 = getUserNamePure(10) 20 | } 21 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/WriterExample.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats._, cats.implicits._, cats.data._ 4 | 5 | object WriterExample extends App { 6 | def add(a: Double, b: Double): Writer[List[String], Double] = 7 | for { 8 | _ <- Writer.tell(List(s"Adding $a to $b")) 9 | res = a + b 10 | _ <- Writer.tell(List(s"The result of the operation is $res")) 11 | } yield res 12 | 13 | println(add(1, 2)) // WriterT((List(Adding 1.0 to 2.0, The result of the operation is 3.0),3.0)) 14 | 15 | } -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/CirceImplicits.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | 3 | import cats._, cats.effect._ 4 | import org.http4s._ 5 | import io.circe._ 6 | 7 | 8 | trait CirceImplicits { 9 | 10 | implicit def jsonEncoderOfImpl[ 11 | F[_]: Applicative 12 | , A : Encoder](implicit ee: EntityEncoder[F, String]): EntityEncoder[F, A] 13 | = org.http4s.circe.jsonEncoderOf[F, A](ee, Applicative[F], Encoder[A]) 14 | 15 | implicit def jsonOfImpl[ 16 | F[_]: Effect 17 | , A : Decoder] 18 | = org.http4s.circe.jsonOf[F, A] 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Chapter03/jvm/src/main/scala/jvm/effect/OptionDemo.scala: -------------------------------------------------------------------------------- 1 | package jvm.effect 2 | 3 | import scala.util.Try 4 | 5 | object OptionDemo extends App { 6 | def getUserName(id: Int): Option[String] = 7 | if (Set(1, 2, 3).contains(id)) Some(s"User-$id") 8 | else None 9 | 10 | getUserName(1) match { // "User-1" 11 | case Some(x) => println(x) 12 | case None => println("User not found") 13 | } 14 | 15 | getUserName(10) match { // "User not found" 16 | case Some(x) => println(x) 17 | case None => println("User not found") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter03/jvm/src/main/scala/jvm/effect/TryDemo.scala: -------------------------------------------------------------------------------- 1 | package jvm.effect 2 | 3 | import scala.util.Try 4 | 5 | object TryDemo extends App { 6 | def division(n1: Double, n2: Double): Double = 7 | if (n2 == 0) throw new RuntimeException("Division by zero!") 8 | else n1 / n2 9 | 10 | def pureDivision(n1: Double, n2: Double): Try[Double] = 11 | Try { division(n1, n2) } 12 | 13 | println(pureDivision(1, 0)) // Failure(java.lang.RuntimeException: Division by zero!) 14 | 15 | println(division(1, 0)) // throws java.lang.RuntimeException: Division by zero! 16 | } 17 | -------------------------------------------------------------------------------- /Chapter03/jvm/src/main/java/jvm/mapping/MappingImperative.java: -------------------------------------------------------------------------------- 1 | package jvm.mapping; 2 | 3 | import java.util.List; 4 | import java.util.ArrayList; 5 | 6 | public class MappingImperative { 7 | public static void main(String[] args) { 8 | // Source collection 9 | List numbers = new ArrayList(); 10 | numbers.add(1); 11 | numbers.add(2); 12 | numbers.add(3); 13 | 14 | // Squared numbers 15 | List result = new ArrayList(); 16 | for (Integer n: numbers) 17 | result.add(n * n); 18 | 19 | System.out.println(result); 20 | } 21 | } -------------------------------------------------------------------------------- /Chapter02/jvm/src/main/scala/jvm/higherorder/While.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object While { 4 | def main(args: Array[String]): Unit = { 5 | var i = 0 6 | while (i < 5) { 7 | println(s"Printing from built-in while loop. Iteration: $i") 8 | i += 1 9 | } 10 | 11 | var j = 0 12 | whileDiy (j < 5) { 13 | println(s"Printing from custom while loop. Iteration: $j") 14 | j += 1 15 | } 16 | } 17 | 18 | @annotation.tailrec 19 | def whileDiy(predicate: => Boolean)(body: => Unit): Unit = 20 | if (predicate) { 21 | body 22 | whileDiy(predicate)(body) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter01/cpp/nested-loop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int main() { 5 | int rows = 3; 6 | int cols = 3; 7 | int matrix[3][3] = { 8 | { 1, 2, 3 }, 9 | { 4, 5, 6 }, 10 | { 7, 8, 9 } 11 | }; 12 | // memset( matrix, 0, rows*cols*sizeof(int) ); 13 | // matrix 14 | 15 | int r = 0; 16 | row_loop: 17 | if (r < rows) { 18 | int c = 0; 19 | col_loop: 20 | if (c < cols) { 21 | cout << matrix[r][c] << " "; 22 | c++; 23 | goto col_loop; 24 | } 25 | 26 | cout << "\n"; 27 | 28 | r++; 29 | goto row_loop; 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /Chapter02/jvm/src/main/scala/jvm/soda/SodaFunctional.scala: -------------------------------------------------------------------------------- 1 | package jvm.soda 2 | 3 | object MainScala { 4 | def main(args: Array[String]): Unit = { 5 | val sm = SodaMachine(2) 6 | 7 | val (sm1, can1) = insertCoin(sm) 8 | val (sm2, can2) = insertCoin(sm1) 9 | 10 | can2.drink() 11 | val (sm3, can3) = insertCoin(sm2) // Out of soda cans 12 | } 13 | 14 | def insertCoin(sm: SodaMachine): (SodaMachine, SodaCan) = 15 | if (sm.cans > 0) (SodaMachine(sm.cans - 1, sm.coins + 1), new SodaCan) 16 | else throw new RuntimeException("Out of soda cans!") 17 | } 18 | 19 | case class SodaMachine(cans: Int, coins: Int = 0) 20 | -------------------------------------------------------------------------------- /Chapter01/jvm/src/main/java/jvm/MainJava.java: -------------------------------------------------------------------------------- 1 | package jvm; 2 | 3 | import java.util.List; 4 | import java.util.ArrayList; 5 | 6 | public class MainJava { 7 | public static void main(String[] args) { 8 | // Source collection 9 | List employees = new ArrayList(); 10 | employees.add("Ann"); 11 | employees.add("John"); 12 | employees.add("Amos"); 13 | employees.add("Jack"); 14 | 15 | // Those employees with their names starting with 'A' 16 | List result = new ArrayList(); 17 | for (String e: employees) 18 | if (e.charAt(0) == 'A') result.add(e); 19 | 20 | System.out.println(result); 21 | } 22 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | postgres: 4 | container_name: mastering_postgres 5 | build: postgres 6 | ports: 7 | - 5432:5432 8 | # volumes: 9 | # - ./_volumes/postgres:/var/lib/postgresql/data 10 | backend: 11 | container_name: mastering_backend 12 | build: . 13 | ports: 14 | - 8888:8888 15 | volumes: 16 | - ./_volumes/ivy2:/root/.ivy2 17 | - ./_volumes/sbt-boot:/root/.sbt/boot 18 | - ./_volumes/coursier:/root/.cache 19 | - .:/root/examples 20 | environment: 21 | - POSTGRES_HOST=postgres 22 | - POSTGRES_PORT=5432 23 | stdin_open: true 24 | tty: true 25 | -------------------------------------------------------------------------------- /Chapter05/jvm/src/main/scala/jvm/Division.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.util.{ Try, Success, Failure } 4 | 5 | 6 | object Division extends App { 7 | def imperativeDivision(n1: Double, n2: Double): Double = 8 | if (n2 == 0) throw new RuntimeException("Division by zero!") 9 | else n1 / n2 10 | 11 | def functionalDivision(n1: Double, n2: Double): Try[Double] = 12 | if (n2 == 0) Failure(new RuntimeException("Division by zero!")) 13 | else Success(n1 / n2) 14 | 15 | try imperativeDivision(1, 0) // Exception is thrown 16 | catch { case x: RuntimeException => x.printStackTrace() } 17 | 18 | println(functionalDivision(1, 0)) // Failure 19 | } 20 | -------------------------------------------------------------------------------- /Chapter05/jvm/src/main/scala/jvm/SignatureDemos.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | 4 | object SignatureDemos extends App { 5 | class Dummy(val id: Int) { 6 | val f: Int => String = x => s"Number: $x; Dummy: $id" 7 | } 8 | 9 | // val f: Int => String = x => s"Number: $x; Dummy: $id" // No `id` in scope, does not compile 10 | 11 | val f1: Dummy => (Int => String) = d => (x => s"Number: $x; Dummy: ${d.id}") 12 | val f2: Int => (Dummy => String) = x => (d => s"Number: $x; Dummy: ${d.id}") 13 | 14 | println(new Dummy(1).f(2)) // Number: 2; Dummy: 1 15 | println(f1(new Dummy(1))(2)) // Number: 2; Dummy: 1 16 | println(f2(2)(new Dummy(1))) // Number: 2; Dummy: 1 17 | } 18 | -------------------------------------------------------------------------------- /Chapter02/jvm/src/main/scala/jvm/lambda/Lambda.scala: -------------------------------------------------------------------------------- 1 | package jvm.lambda 2 | 3 | object Lambda { 4 | def main(args: Array[String]): Unit = { 5 | case class Cookie(name: String, gender: String) 6 | 7 | def greeting(cookie: Cookie)(modifier: (String, String) => String): Unit = { 8 | val name = cookie.name 9 | val gender = cookie.gender 10 | val modifiedName = modifier(name, gender) 11 | print(s"Hello, $modifiedName") 12 | } 13 | 14 | def isPhd(name: String): Boolean = name == "Smith" 15 | 16 | val cookie = Cookie("Smith", "male") 17 | 18 | greeting(cookie) { (name, gender) => 19 | if (isPhd(name)) s"Dr $name" 20 | else gender match { 21 | case "male" => s"Mr $name" 22 | case "female" => s"Mrs $name" 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/db/order.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | package db 3 | 4 | import cats.{ Order => COrder, _ }, cats.implicits._, cats.effect._, cats.data._ 5 | import doobie._, doobie.implicits._ 6 | 7 | import infrastructure.tr 8 | 9 | object order extends OrderDbHelpers { 10 | def create(o: Order): IO[Int] = 11 | sql""" 12 | insert into "order" (customer, good) 13 | values (${o.customer}, ${o.good}) 14 | """ 15 | .update.withUniqueGeneratedKeys[Int]("id").transact(tr) 16 | 17 | def list: IO[List[Order]] = 18 | selectOrderSql.query[Order].to[List].transact(tr) 19 | 20 | def get(id: Int): IO[Order] = 21 | (selectOrderSql ++ sql"where id = $id") 22 | .query[Order].unique.transact(tr) 23 | } 24 | 25 | trait OrderDbHelpers { 26 | val selectOrderSql = fr"""select * from "order"""" 27 | } 28 | -------------------------------------------------------------------------------- /Chapter07/jvm/src/main/scala/jvm/WebAPI.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object WebAPI { 4 | // Domain model 5 | case class FullUser(name: String, id: Int, passwordHash: String) 6 | case class ShortUser(name: String, id: Int) 7 | 8 | val rootUser = FullUser("root", 0, "acbd18db4cc2f85cedef654fccc4a4d8") 9 | 10 | // Respond to the current request with the domain object 11 | def respondWith(user: ShortUser): Unit = ??? 12 | 13 | // Converter to convert between full user and short user 14 | implicit def full2short(u: FullUser): ShortUser = 15 | ShortUser(u.name, u.id) 16 | 17 | // Request handler 18 | val handlerExplicit: PartialFunction[String, Unit] = { 19 | case "/root_user" => respondWith(full2short(rootUser)) 20 | } 21 | 22 | val handlerImplicit: PartialFunction[String, Unit] = { 23 | case "/root_user" => respondWith(rootUser) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter11/jvm/log.txt: -------------------------------------------------------------------------------- 1 | Read 6 lines from input 2 | Computing the following stats: avg=true, max=true, min=true 3 | Computing the following stats: avg=true, max=true, min=true 4 | Read the input file to compute statistics on it 5 | Average Price: 940.0 6 | Maximal Price: 2500.0 7 | Minimal Price: 100.0 8 | Computing the following stats: avg=true, max=true, min=true 9 | Computing the following stats: avg=true, max=true, min=true 10 | Computing the following stats: avg=true, max=true, min=true 11 | Computing the following stats: avg=true, max=true, min=true 12 | Computing the following stats: avg=true, max=true, min=true 13 | Computing the following stats: avg=true, max=true, min=true 14 | Computing the following stats: avg=true, max=true, min=true 15 | Computing the following stats: avg=true, max=true, min=true 16 | Read 5 lines from input 17 | Computing the following stats: avg=true, max=true, min=true 18 | Read the input file to compute statistics on it 19 | Average Price: 940.0 20 | Maximal Price: 2500.0 21 | Minimal Price: 100.0 22 | -------------------------------------------------------------------------------- /Chapter01/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | lazy val commonSettings = Seq( 11 | name := "jvm" 12 | , version := "0.1.0" 13 | , scalaVersion := ScalaVer 14 | 15 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 16 | , scalacOptions ++= Seq( 17 | "-deprecation" 18 | , "-encoding", "UTF-8" 19 | , "-feature" 20 | , "-language:existentials" 21 | , "-language:higherKinds" 22 | , "-language:implicitConversions" 23 | , "-language:experimental.macros" 24 | , "-unchecked" 25 | // , "-Xfatal-warnings" 26 | , "-Xlint" 27 | // , "-Yinline-warnings" 28 | , "-Ywarn-dead-code" 29 | , "-Xfuture" 30 | , "-Ypartial-unification") 31 | ) 32 | 33 | lazy val root = (project in file(".")) 34 | .settings(commonSettings) 35 | .settings( 36 | initialCommands := "import jvm._, Main._" 37 | ) 38 | -------------------------------------------------------------------------------- /Chapter02/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | lazy val commonSettings = Seq( 11 | name := "jvm" 12 | , version := "0.1.0" 13 | , scalaVersion := ScalaVer 14 | 15 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 16 | , scalacOptions ++= Seq( 17 | "-deprecation" 18 | , "-encoding", "UTF-8" 19 | , "-feature" 20 | , "-language:existentials" 21 | , "-language:higherKinds" 22 | , "-language:implicitConversions" 23 | , "-language:experimental.macros" 24 | , "-unchecked" 25 | // , "-Xfatal-warnings" 26 | , "-Xlint" 27 | // , "-Yinline-warnings" 28 | , "-Ywarn-dead-code" 29 | , "-Xfuture" 30 | , "-Ypartial-unification") 31 | ) 32 | 33 | lazy val root = (project in file(".")) 34 | .settings(commonSettings) 35 | .settings( 36 | initialCommands := "import jvm._, Main._" 37 | ) 38 | -------------------------------------------------------------------------------- /Chapter03/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | lazy val commonSettings = Seq( 11 | name := "jvm" 12 | , version := "0.1.0" 13 | , scalaVersion := ScalaVer 14 | 15 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 16 | , scalacOptions ++= Seq( 17 | "-deprecation" 18 | , "-encoding", "UTF-8" 19 | , "-feature" 20 | , "-language:existentials" 21 | , "-language:higherKinds" 22 | , "-language:implicitConversions" 23 | , "-language:experimental.macros" 24 | , "-unchecked" 25 | // , "-Xfatal-warnings" 26 | , "-Xlint" 27 | // , "-Yinline-warnings" 28 | , "-Ywarn-dead-code" 29 | , "-Xfuture" 30 | , "-Ypartial-unification") 31 | ) 32 | 33 | lazy val root = (project in file(".")) 34 | .settings(commonSettings) 35 | .settings( 36 | initialCommands := "import jvm._, Main._" 37 | ) 38 | -------------------------------------------------------------------------------- /Chapter08/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "1.1.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | lazy val commonSettings = Seq( 11 | name := "jvm" 12 | , version := "0.1.0" 13 | , scalaVersion := ScalaVer 14 | 15 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 16 | , scalacOptions ++= Seq( 17 | "-deprecation" 18 | , "-encoding", "UTF-8" 19 | , "-feature" 20 | , "-language:existentials" 21 | , "-language:higherKinds" 22 | , "-language:implicitConversions" 23 | , "-language:experimental.macros" 24 | , "-unchecked" 25 | // , "-Xfatal-warnings" 26 | , "-Xlint" 27 | // , "-Yinline-warnings" 28 | , "-Ywarn-dead-code" 29 | , "-Xfuture" 30 | , "-Ypartial-unification") 31 | 32 | , libraryDependencies ++= Seq( 33 | "org.typelevel" %% "cats-core" % Cats 34 | ) 35 | ) 36 | 37 | lazy val root = (project in file(".")) 38 | .settings(commonSettings) 39 | .settings( 40 | initialCommands := "import jvm._, Main._" 41 | ) 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter04/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | val Akka = "2.5.12" 11 | 12 | lazy val commonSettings = Seq( 13 | name := "jvm" 14 | , version := "0.1.0" 15 | , scalaVersion := ScalaVer 16 | 17 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 18 | , scalacOptions ++= Seq( 19 | "-deprecation" 20 | , "-encoding", "UTF-8" 21 | , "-feature" 22 | , "-language:existentials" 23 | , "-language:higherKinds" 24 | , "-language:implicitConversions" 25 | , "-language:experimental.macros" 26 | , "-unchecked" 27 | // , "-Xfatal-warnings" 28 | , "-Xlint" 29 | // , "-Yinline-warnings" 30 | , "-Ywarn-dead-code" 31 | , "-Xfuture" 32 | , "-Ypartial-unification") 33 | 34 | , libraryDependencies ++= Seq( 35 | "com.typesafe.akka" %% "akka-actor" % Akka 36 | ) 37 | ) 38 | 39 | lazy val root = (project in file(".")) 40 | .settings(commonSettings) 41 | .settings( 42 | initialCommands := "import jvm._, Main._" 43 | ) 44 | -------------------------------------------------------------------------------- /Chapter05/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | val Akka = "2.5.12" 11 | 12 | lazy val commonSettings = Seq( 13 | name := "jvm" 14 | , version := "0.1.0" 15 | , scalaVersion := ScalaVer 16 | 17 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 18 | , scalacOptions ++= Seq( 19 | "-deprecation" 20 | , "-encoding", "UTF-8" 21 | , "-feature" 22 | , "-language:existentials" 23 | , "-language:higherKinds" 24 | , "-language:implicitConversions" 25 | , "-language:experimental.macros" 26 | , "-unchecked" 27 | // , "-Xfatal-warnings" 28 | , "-Xlint" 29 | // , "-Yinline-warnings" 30 | , "-Ywarn-dead-code" 31 | , "-Xfuture" 32 | , "-Ypartial-unification") 33 | 34 | , libraryDependencies ++= Seq( 35 | "com.typesafe.akka" %% "akka-actor" % Akka 36 | ) 37 | ) 38 | 39 | lazy val root = (project in file(".")) 40 | .settings(commonSettings) 41 | .settings( 42 | initialCommands := "import jvm._, Main._" 43 | ) 44 | -------------------------------------------------------------------------------- /Chapter06/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | val Akka = "2.5.12" 11 | 12 | lazy val commonSettings = Seq( 13 | name := "jvm" 14 | , version := "0.1.0" 15 | , scalaVersion := ScalaVer 16 | 17 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 18 | , scalacOptions ++= Seq( 19 | "-deprecation" 20 | , "-encoding", "UTF-8" 21 | , "-feature" 22 | , "-language:existentials" 23 | , "-language:higherKinds" 24 | , "-language:implicitConversions" 25 | , "-language:experimental.macros" 26 | , "-unchecked" 27 | // , "-Xfatal-warnings" 28 | , "-Xlint" 29 | // , "-Yinline-warnings" 30 | , "-Ywarn-dead-code" 31 | , "-Xfuture" 32 | , "-Ypartial-unification") 33 | 34 | , libraryDependencies ++= Seq( 35 | "com.typesafe.akka" %% "akka-actor" % Akka 36 | ) 37 | ) 38 | 39 | lazy val root = (project in file(".")) 40 | .settings(commonSettings) 41 | .settings( 42 | initialCommands := "import jvm._, Main._" 43 | ) 44 | -------------------------------------------------------------------------------- /Chapter07/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "0.9.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | val Akka = "2.5.12" 11 | 12 | lazy val commonSettings = Seq( 13 | name := "jvm" 14 | , version := "0.1.0" 15 | , scalaVersion := ScalaVer 16 | 17 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 18 | , scalacOptions ++= Seq( 19 | "-deprecation" 20 | , "-encoding", "UTF-8" 21 | , "-feature" 22 | , "-language:existentials" 23 | , "-language:higherKinds" 24 | , "-language:implicitConversions" 25 | , "-language:experimental.macros" 26 | , "-unchecked" 27 | // , "-Xfatal-warnings" 28 | , "-Xlint" 29 | // , "-Yinline-warnings" 30 | , "-Ywarn-dead-code" 31 | , "-Xfuture" 32 | , "-Ypartial-unification") 33 | 34 | , libraryDependencies ++= Seq( 35 | "com.typesafe.akka" %% "akka-actor" % Akka 36 | ) 37 | ) 38 | 39 | lazy val root = (project in file(".")) 40 | .settings(commonSettings) 41 | .settings( 42 | initialCommands := "import jvm._, Main._" 43 | ) 44 | -------------------------------------------------------------------------------- /Chapter10/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "1.1.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | lazy val commonSettings = Seq( 11 | name := "jvm" 12 | , version := "0.1.0" 13 | , scalaVersion := ScalaVer 14 | 15 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 16 | , scalacOptions ++= Seq( 17 | "-deprecation" 18 | , "-encoding", "UTF-8" 19 | , "-feature" 20 | , "-language:existentials" 21 | , "-language:higherKinds" 22 | , "-language:implicitConversions" 23 | , "-language:experimental.macros" 24 | , "-unchecked" 25 | // , "-Xfatal-warnings" 26 | , "-Xlint" 27 | // , "-Yinline-warnings" 28 | , "-Ywarn-dead-code" 29 | , "-Xfuture" 30 | , "-Ypartial-unification") 31 | 32 | , libraryDependencies ++= Seq( 33 | "org.typelevel" %% "cats-core" % Cats 34 | ) 35 | 36 | , libraryDependencies += "commons-io" % "commons-io" % "2.6" 37 | ) 38 | 39 | lazy val root = (project in file(".")) 40 | .settings(commonSettings) 41 | .settings( 42 | initialCommands := "import jvm._, Main._" 43 | ) 44 | -------------------------------------------------------------------------------- /Chapter09/bash/client.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | function createCustomer { 5 | if [[ $# -ne 1 ]]; then 6 | echo "Usage: ./client.sh cc customer_name" 7 | exit 1 8 | else 9 | curl -vX POST http://localhost:8888/customer \ 10 | -d "{\"name\": \"$1\"}" 11 | echo 12 | fi 13 | } 14 | 15 | function placeOrder { 16 | if [[ $# -ne 2 ]]; then 17 | echo "Usage: ./client.sh po customer_id good_id" 18 | exit 1 19 | else 20 | curl -vX POST http://localhost:8888/order \ 21 | --cookie "shop_customer_id=$1" \ 22 | -d "{\"good\": $2}" 23 | echo 24 | fi 25 | } 26 | 27 | function listOrders { 28 | curl -vX GET http://localhost:8888/order 29 | echo 30 | } 31 | 32 | function listGoods { 33 | curl -vX GET http://localhost:8888/good 34 | echo 35 | } 36 | 37 | function help { 38 | echo "Usage: ./client.sh command [arguments] 39 | Possible commands: 40 | * cc - create customer 41 | * po - place order 42 | * lo - list orders 43 | * lg - list goods " 44 | } 45 | 46 | case $1 in 47 | cc) createCustomer "${@:2}";; 48 | po) placeOrder "${@:2}";; 49 | lo) listOrders "${@:2}";; 50 | lg) listGoods "${@:2}";; 51 | *) help;; 52 | esac 53 | -------------------------------------------------------------------------------- /Chapter11/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.3" 2 | 3 | val Cats = "1.1.0" 4 | val CatsEffect = "0.3" 5 | val KindProjector = "0.9.4" 6 | 7 | val ScalaTest = "3.0.4" 8 | val ScalaCheck = "1.13.5" 9 | 10 | val CommonsIO = "2.6" 11 | 12 | lazy val commonSettings = Seq( 13 | name := "jvm" 14 | , version := "0.1.0" 15 | , scalaVersion := ScalaVer 16 | 17 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 18 | , scalacOptions ++= Seq( 19 | "-deprecation" 20 | , "-encoding", "UTF-8" 21 | , "-feature" 22 | , "-language:existentials" 23 | , "-language:higherKinds" 24 | , "-language:implicitConversions" 25 | , "-language:experimental.macros" 26 | , "-unchecked" 27 | // , "-Xfatal-warnings" 28 | , "-Xlint" 29 | // , "-Yinline-warnings" 30 | , "-Ywarn-dead-code" 31 | , "-Xfuture" 32 | , "-Ypartial-unification") 33 | 34 | , libraryDependencies ++= Seq( 35 | "org.typelevel" %% "cats-core" % Cats 36 | , "commons-io" % "commons-io" % CommonsIO 37 | ) 38 | ) 39 | 40 | lazy val root = (project in file(".")) 41 | .settings(commonSettings) 42 | .settings( 43 | initialCommands := "import jvm._, Main._" 44 | ) 45 | -------------------------------------------------------------------------------- /Chapter12/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.6" 2 | 3 | val Cats = "1.1.0" 4 | val CatsEffect = "1.0.0-RC2-93ac33d" 5 | val KindProjector = "0.9.7" 6 | val Akka = "2.5.14" 7 | val SLF4J = "1.7.25" 8 | 9 | lazy val commonSettings = Seq( 10 | name := "jvm" 11 | , version := "0.1.0" 12 | , scalaVersion := ScalaVer 13 | 14 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 15 | , scalacOptions ++= Seq( 16 | "-deprecation" 17 | , "-encoding", "UTF-8" 18 | , "-feature" 19 | , "-language:existentials" 20 | , "-language:higherKinds" 21 | , "-language:implicitConversions" 22 | , "-language:experimental.macros" 23 | , "-unchecked" 24 | // , "-Xfatal-warnings" 25 | // , "-Xlint" 26 | // , "-Yinline-warnings" 27 | , "-Ywarn-dead-code" 28 | , "-Xfuture" 29 | , "-Ypartial-unification") 30 | 31 | , libraryDependencies ++= Seq( 32 | "org.typelevel" %% "cats-core" % Cats 33 | , "org.typelevel" %% "cats-effect" % CatsEffect 34 | 35 | , "com.typesafe.akka" %% "akka-actor" % Akka 36 | 37 | , "org.slf4j" % "slf4j-simple" % SLF4J 38 | ) 39 | ) 40 | 41 | lazy val root = (project in file(".")) 42 | .settings(commonSettings) 43 | 44 | -------------------------------------------------------------------------------- /Chapter13/src/main/scala/crawler/Main.scala: -------------------------------------------------------------------------------- 1 | package crawler 2 | 3 | import java.net.URL 4 | 5 | import scala.util.Try 6 | import scala.collection.JavaConverters._ 7 | 8 | import org.jsoup.nodes._ 9 | import org.jsoup._ 10 | 11 | object Main { 12 | def fetch(url: URL): Option[Set[URL]] = Try { 13 | Jsoup.connect(url.toString) 14 | .get 15 | .getElementsByAttribute("href") 16 | .asScala.map( h => new URL(url, h.attr("href")) ).toSet 17 | }.toOption 18 | 19 | def fetchToDepth(url: URL, depth: Int, visited: Set[URL] = Set()): Set[URL] = { 20 | val links = fetch(url).getOrElse(Set()) 21 | 22 | if (depth > 0) links ++ links 23 | .filter(!visited(_)) 24 | .toList 25 | .zipWithIndex 26 | .foldLeft(Set[URL]()) { case (accum, (next, id)) => 27 | println(s"Progress for depth $depth: $id of ${links.size}") 28 | accum ++ (if (!accum(next)) fetchToDepth(next, depth - 1, accum) else Set()) 29 | } 30 | .toSet 31 | else links 32 | } 33 | 34 | def main(args: Array[String]): Unit = { 35 | val target = new URL("http://mvnrepository.com/") 36 | 37 | val res = fetchToDepth(target, 1) 38 | println(res.take(10).mkString("\n")) 39 | println(res.size) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/TailRecM.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats._, cats.implicits._, cats.data._ 4 | 5 | object TailRecM extends App { 6 | implicit val simpleWriterMonad: Monad[SimpleWriter] = new Monad[SimpleWriter] { 7 | override def map[A, B](fa: SimpleWriter[A])(f: A => B): SimpleWriter[B] = 8 | fa.copy(value = f(fa.value)) 9 | 10 | override def flatMap[A, B](fa: SimpleWriter[A])(f: A => SimpleWriter[B]): SimpleWriter[B] = { 11 | val res = f(fa.value) 12 | SimpleWriter(fa.log ++ res.log, res.value) 13 | } 14 | 15 | override def pure[A](a: A): SimpleWriter[A] = SimpleWriter(Nil, a) 16 | 17 | // @annotation.tailrec 18 | // override def tailRecM[A, B](a: A)(f: A => SimpleWriter[Either[A,B]]): SimpleWriter[B] = 19 | // f(a).flatMap { 20 | // case Left (a1) => tailRecM(a1)(f) 21 | // case Right(res) => pure(res) 22 | // } 23 | 24 | @annotation.tailrec 25 | override def tailRecM[A, B](a: A)(f: A => SimpleWriter[Either[A,B]]): SimpleWriter[B] = { 26 | val next = f(a) 27 | next.value match { 28 | case Left (a1) => tailRecM(a1)(f) 29 | case Right(res) => pure(res) 30 | } 31 | } 32 | } 33 | 34 | Monad[SimpleWriter].tailRecM[Int, Unit](0) { a => Monad[SimpleWriter].pure(Left(a))} 35 | } -------------------------------------------------------------------------------- /Chapter05/jvm/src/main/scala/jvm/FunctionalAbstractions.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.util.{ Try, Success, Failure } 4 | 5 | 6 | object FunctionalAbstractions extends App { 7 | def divide(n1: Double, n2: Double): Try[Double] = 8 | if (n2 == 0) Failure(new RuntimeException("Division by zero!")) 9 | else Success(n1 / n2) 10 | 11 | def f1Match(x: Double): Try[Double] = 12 | divide(2, x) match { 13 | case Success(res) => Success(res + 3) 14 | case f@Failure(_) => f 15 | } 16 | 17 | def f1Map(x: Double): Try[Double] = 18 | divide(2, x).map(r => r + 3) 19 | 20 | def f2Match(x: Double, y: Double): Try[Double] = 21 | divide(2, x) match { 22 | case Success(r1) => divide(r1, y) match { 23 | case Success(r2) => Success(r2 + 3) 24 | case f@Failure(_) => f 25 | } 26 | case f@Failure(_) => f 27 | } 28 | 29 | def f2FlatMap(x: Double, y: Double): Try[Double] = 30 | divide(2, x).flatMap(r1 => divide(r1, y)) 31 | .map(r2 => r2 + 3) 32 | 33 | println(f1Match(2)) // 4.0 34 | println(f1Match(0)) // Failure 35 | 36 | println(f1Map(2)) // 4.0 37 | println(f1Map(0)) // Failure 38 | 39 | println(f2Match(2, 2)) // 3.5 40 | println(f2Match(2, 0)) // Failure 41 | 42 | println(f2FlatMap(2, 2)) // 3.5 43 | println(f2FlatMap(2, 0)) // Failure 44 | } 45 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/MonoidEither.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats._, cats.syntax.all._ 4 | 5 | object MonoidEither extends App { 6 | 7 | implicit def applicative[L: Monoid]: Applicative[Either[L, ?]] = new Applicative[Either[L, ?]] { 8 | override def ap[A, B](ff: Either[L, A => B])(fa: Either[L, A]): Either[L, B] = (ff, fa) match { 9 | case (Right(f), Right(a)) => Right(f(a)) 10 | case (Left(e1), Left(e2)) => Left(e1 |+| e2) 11 | case (Left(e), _) => Left(e) 12 | case (_, Left(e)) => Left(e) 13 | } 14 | 15 | override def pure[A](a: A): Either[L, A] = Right(a) 16 | } 17 | 18 | val semigroupInt: Semigroup[Int] = new Semigroup[Int] { 19 | override def combine(a: Int, b: Int) = a + b 20 | } 21 | 22 | def monoidInt: Monoid[Int] = new Monoid[Int] { 23 | override def combine(a: Int, b: Int) = a + b 24 | override def empty = 0 25 | } 26 | 27 | implicit def monoidIntMult: Monoid[Int] = new Monoid[Int] { 28 | override def combine(a: Int, b: Int) = a * b 29 | override def empty = 1 30 | } 31 | 32 | println(2 |+| 3) // 6 33 | println(2 combine 3) // 6 34 | 35 | implicit val listMonoid: MonoidK[List] = new MonoidK[List] { 36 | override def combineK[A](a1: List[A], a2: List[A]): List[A] = 37 | a1 ++ a2 38 | 39 | override def empty[A] = Nil 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/db/good.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | package db 3 | 4 | import cats._, cats.implicits._, cats.effect._, cats.data._ 5 | import doobie._, doobie.implicits._ 6 | 7 | import infrastructure.tr 8 | 9 | object good extends GoodDbHelpers { 10 | def create(c: Good): IO[Int] = 11 | sql""" 12 | insert into good ( 13 | name 14 | , price 15 | , stock) 16 | values ( 17 | ${c.name} 18 | , ${c.price} 19 | , ${c.stock}) 20 | """ 21 | .update.withUniqueGeneratedKeys[Int]("id").transact(tr) 22 | 23 | def findByName(name: String): IO[Option[Good]] = 24 | (selectGoodSql ++ sql"""where name = $name""") 25 | .query[Good].option.transact(tr) 26 | 27 | def list: IO[List[Good]] = 28 | selectGoodSql.query[Good].to[List].transact(tr) 29 | 30 | def get(id: Int): IO[Good] = 31 | (selectGoodSql ++ sql"where id = $id") 32 | .query[Good].unique.transact(tr) 33 | 34 | def update(c: Good): IO[Int] = 35 | sql""" 36 | update good set 37 | name = ${c.name } 38 | , price = ${c.price} 39 | , stock = ${c.stock} 40 | where id = ${c.id} 41 | """ 42 | .update.run.transact(tr) 43 | 44 | def delete(id: Int): IO[Int] = 45 | sql"""delete from good where id = $id""" 46 | .update.run.transact(tr) 47 | } 48 | 49 | trait GoodDbHelpers { 50 | val selectGoodSql = fr"select * from good" 51 | } 52 | -------------------------------------------------------------------------------- /postgres/schema.sql: -------------------------------------------------------------------------------- 1 | -- Drop table 2 | 3 | -- DROP TABLE public.customer 4 | 5 | CREATE TABLE customer ( 6 | id serial NOT NULL, 7 | "name" varchar NOT NULL, 8 | CONSTRAINT customer_pk PRIMARY KEY (id), 9 | CONSTRAINT customer_un UNIQUE (name) 10 | ) 11 | WITH ( 12 | OIDS=FALSE 13 | ) ; 14 | CREATE UNIQUE INDEX customer_name_idx ON public.customer USING btree (name) ; 15 | 16 | -- Drop table 17 | 18 | -- DROP TABLE public.good 19 | 20 | CREATE TABLE good ( 21 | id serial NOT NULL, 22 | "name" varchar NOT NULL, 23 | price float4 NOT NULL, 24 | stock int4 NOT NULL DEFAULT 0, 25 | CONSTRAINT good_pk PRIMARY KEY (id) 26 | ) 27 | WITH ( 28 | OIDS=FALSE 29 | ) ; 30 | 31 | -- Drop table 32 | 33 | -- DROP TABLE public."order" 34 | 35 | CREATE TABLE "order" ( 36 | id serial NOT NULL, 37 | customer int4 NOT NULL, 38 | good int4 NOT NULL, 39 | CONSTRAINT store_order_pk PRIMARY KEY (id), 40 | CONSTRAINT order_customer_fk FOREIGN KEY (customer) REFERENCES customer(id) ON DELETE CASCADE, 41 | CONSTRAINT order_good_fk FOREIGN KEY (good) REFERENCES good(id) ON DELETE CASCADE 42 | ) 43 | WITH ( 44 | OIDS=FALSE 45 | ) ; 46 | 47 | INSERT INTO good (id, name, price, stock) VALUES(1, 'MacBook Pro 15''', 2500, 15); 48 | INSERT INTO good (id, name, price, stock) VALUES(2, 'iPhone 10', 1000, 10); 49 | INSERT INTO good (id, name, price, stock) VALUES(3, 'MacBook Air', 900, 3); 50 | INSERT INTO good (id, name, price, stock) VALUES(4, 'Samsung Galaxy S5', 500, 8); 51 | INSERT INTO good (id, name, price, stock) VALUES(5, 'Panasonic Camera', 120, 34); 52 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/GameActors.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import akka.actor._ 4 | 5 | 6 | object GameActors extends App { 7 | val system = ActorSystem("GameActors") 8 | 9 | val notifications = system.actorOf(Props[NotificationsActor], name = "notifications") 10 | val gameState = system.actorOf(Props(classOf[GameState], notifications), name = "gameState") 11 | 12 | val u1 = User("User1", 10) 13 | val u2 = User("User2", 100) 14 | 15 | gameState ! Connect(u1) 16 | gameState ! Connect(u2) 17 | gameState ! Round 18 | gameState ! Disconnect(u2) 19 | } 20 | 21 | case class User(name: String, score: Int) 22 | 23 | sealed trait Protocol 24 | case class Connect (user : User ) extends Protocol 25 | case class Disconnect (user : User ) extends Protocol 26 | case class RewardWinners(users: Seq[User]) extends Protocol 27 | case object Round extends Protocol 28 | 29 | class GameState(notifications: ActorRef) extends Actor { 30 | val onlineUsers = collection.mutable.ListBuffer[User]() 31 | 32 | def receive = { 33 | case Connect (u) => onlineUsers += u 34 | case Disconnect(u) => onlineUsers -= u 35 | case Round => notifications ! RewardWinners(onlineUsers) 36 | } 37 | } 38 | 39 | class NotificationsActor extends Actor { 40 | def receive = { 41 | case RewardWinners(users) => 42 | Thread.sleep(1000) 43 | val winners = users.filter(_.score >= 100) 44 | if (winners.nonEmpty) winners.foreach { u => 45 | println(s"User $u is rewarded!") } 46 | else println("No one to reward!") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/Resources.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.language.postfixOps 4 | import cats._, cats.implicits._, cats.effect._, cats.data._ 5 | 6 | object ProductRight extends App { 7 | def foo = IO[Int] { println("Foo"); 1 } 8 | def bar = IO[Int] { println("Bar"); 2 } 9 | 10 | println( (foo *> bar).unsafeRunSync ) 11 | } 12 | 13 | object Bracket extends App { 14 | class DBSession { 15 | var closed = false 16 | def runStatement(stat: String): IO[List[String]] = { 17 | val computation = IO { 18 | if (stat.contains("user")) List("John", "Ann") 19 | else if (stat.contains("post")) List("Post1", "Post2") 20 | else Nil 21 | } 22 | if (!closed) computation 23 | else IO.raiseError { new RuntimeException("Connection is closed") } 24 | } 25 | def close(): Unit = closed = true 26 | def isClosed = closed 27 | } 28 | 29 | def dbSession: IO[DBSession] = IO { new DBSession } 30 | 31 | def selectUsers(db: DBSession): IO[List[String]] = 32 | dbSession.flatMap(_.runStatement("select * from user")) 33 | 34 | var sessIntercept: DBSession = null 35 | val computation: IO[Unit] = 36 | dbSession.bracket(sess => for { 37 | users <- selectUsers(sess) 38 | 39 | _ = println(s"Users:\n${users.mkString("\n")}") 40 | _ = sessIntercept = sess 41 | } yield ())(sess => IO { sess.close() }) 42 | 43 | println(s"Session intercept before execution: $sessIntercept") 44 | computation.unsafeRunSync 45 | println(s"Session intercept after execution: $sessIntercept") 46 | println(s"Session intercept closed status: ${sessIntercept.isClosed}") 47 | } 48 | -------------------------------------------------------------------------------- /Chapter04/jvm/src/main/scala/jvm/GameActorsImmutable.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import akka.actor._ 4 | 5 | 6 | object GameActorsImmutable extends App { 7 | case class User(name: String, score: Int) 8 | 9 | sealed trait Protocol 10 | case class Connect (user : User ) extends Protocol 11 | case class Disconnect (user : User ) extends Protocol 12 | case class RewardWinners(users: Seq[User]) extends Protocol 13 | case object Round extends Protocol 14 | 15 | class GameState(notifications: ActorRef) extends Actor { 16 | var onlineUsers = List[User]() 17 | 18 | def receive = { 19 | case Connect (u) => onlineUsers :+= u 20 | case Disconnect(u) => onlineUsers = onlineUsers.filter(_ != u) 21 | case Round => notifications ! RewardWinners(onlineUsers) 22 | } 23 | } 24 | 25 | class NotificationsActor extends Actor { 26 | def receive = { 27 | case RewardWinners(users) => 28 | Thread.sleep(1000) 29 | val winners = users.filter(_.score >= 100) 30 | if (winners.nonEmpty) winners.foreach { u => 31 | println(s"User $u is rewarded!") } 32 | else println("No one to reward!") 33 | } 34 | } 35 | 36 | 37 | val system = ActorSystem("GameActors") 38 | 39 | val notifications = system.actorOf(Props[NotificationsActor], name = "notifications") 40 | val gameState = system.actorOf(Props(classOf[GameState], notifications), name = "gameState") 41 | 42 | val u1 = User("User1", 10) 43 | val u2 = User("User2", 100) 44 | 45 | gameState ! Connect(u1) 46 | gameState ! Connect(u2) 47 | gameState ! Round 48 | gameState ! Disconnect(u2) 49 | } 50 | -------------------------------------------------------------------------------- /Chapter09/jvm/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.6" 2 | 3 | val Cats = "1.1.0" 4 | val CatsEffect = "1.0.0-RC2-93ac33d" 5 | val Doobie = "0.5.3" 6 | val Http4s = "0.18.15" 7 | val Circe = "0.9.3" 8 | val KindProjector = "0.9.7" 9 | 10 | val SLF4J = "1.7.25" 11 | 12 | lazy val commonSettings = Seq( 13 | name := "jvm" 14 | , version := "0.1.0" 15 | , scalaVersion := ScalaVer 16 | 17 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 18 | , scalacOptions ++= Seq( 19 | "-deprecation" 20 | , "-encoding", "UTF-8" 21 | , "-feature" 22 | , "-language:existentials" 23 | , "-language:higherKinds" 24 | , "-language:implicitConversions" 25 | , "-language:experimental.macros" 26 | , "-unchecked" 27 | // , "-Xfatal-warnings" 28 | // , "-Xlint" 29 | // , "-Yinline-warnings" 30 | , "-Ywarn-dead-code" 31 | , "-Xfuture" 32 | , "-Ypartial-unification") 33 | 34 | , libraryDependencies ++= Seq( 35 | "org.typelevel" %% "cats-core" % Cats 36 | , "org.typelevel" %% "cats-effect" % CatsEffect 37 | 38 | , "org.tpolecat" %% "doobie-core" % Doobie 39 | , "org.tpolecat" %% "doobie-postgres" % Doobie 40 | 41 | , "org.http4s" %% "http4s-dsl" % Http4s 42 | , "org.http4s" %% "http4s-circe" % Http4s 43 | , "org.http4s" %% "http4s-blaze-server" % Http4s 44 | , "org.http4s" %% "http4s-blaze-client" % Http4s 45 | 46 | , "io.circe" %% "circe-core" % Circe 47 | , "io.circe" %% "circe-generic" % Circe 48 | , "io.circe" %% "circe-parser" % Circe 49 | 50 | , "org.slf4j" % "slf4j-simple" % SLF4J 51 | ) 52 | ) 53 | 54 | lazy val root = (project in file(".")) 55 | .settings(commonSettings) 56 | 57 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/AdditionMonadic.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats.Monad, cats.syntax.all._ 4 | 5 | object AdditionMonadic extends App { 6 | def add[F[_]](a: Double, b: Double)(implicit M: Monad[F], L: Logging[F]): F[Double] = 7 | for { 8 | _ <- L.log(s"Adding $a to $b") 9 | res = a + b 10 | _ <- L.log(s"The result of the operation is $res") 11 | } yield res 12 | 13 | println(add[SimpleWriter](1, 2)) // SimpleWriter(List(Adding 1.0 to 2.0, The result of the operation is 3.0),3.0) 14 | 15 | } 16 | 17 | case class SimpleWriter[A](log: List[String], value: A) 18 | 19 | object SimpleWriter { 20 | // Wraps a value into SimpleWriter 21 | def pure[A](value: A): SimpleWriter[A] = 22 | SimpleWriter(Nil, value) 23 | 24 | // Wraps a log message into SimpleWriter 25 | def log(message: String): SimpleWriter[Unit] = 26 | SimpleWriter(List(message), ()) 27 | 28 | implicit val monad: Monad[SimpleWriter] = new Monad[SimpleWriter] { 29 | override def map[A, B](fa: SimpleWriter[A])(f: A => B): SimpleWriter[B] = 30 | fa.copy(value = f(fa.value)) 31 | 32 | override def flatMap[A, B](fa: SimpleWriter[A])(f: A => SimpleWriter[B]): SimpleWriter[B] = { 33 | val res = f(fa.value) 34 | SimpleWriter(fa.log ++ res.log, res.value) 35 | } 36 | 37 | override def pure[A](a: A): SimpleWriter[A] = SimpleWriter(Nil, a) 38 | 39 | override def tailRecM[A, B](a: A)(f: A => SimpleWriter[Either[A,B]]): SimpleWriter[B] = ??? 40 | } 41 | 42 | } 43 | 44 | 45 | trait Logging[F[_]] { 46 | def log(msg: String): F[Unit] 47 | } 48 | 49 | object Logging { 50 | implicit val writerLogging: Logging[SimpleWriter] = new Logging[SimpleWriter] { 51 | def log(msg: String) = SimpleWriter.log(msg) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/TraverseExample.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats._, cats.syntax.all._, cats.instances.list._ 4 | 5 | object TraverseExample extends App { 6 | type Fx[A] = Either[List[String], A] 7 | 8 | implicit val applicative: Applicative[Fx] = new Applicative[Fx] { 9 | override def ap[A, B](ff: Fx[A => B])(fa: Fx[A]): Fx[B] = (ff, fa) match { 10 | case (Right(f), Right(a)) => Right(f(a)) 11 | case (Left(e1), Left(e2)) => Left(e1 ++ e2) 12 | case (Left(e), _) => Left(e) 13 | case (_, Left(e)) => Left(e) 14 | } 15 | 16 | override def pure[A](a: A): Fx[A] = Right(a) 17 | } 18 | 19 | implicit val monoidDouble: Monoid[Double] = new Monoid[Double] { 20 | def combine(x1: Double, x2: Double): Double = x1 + x2 21 | def empty: Double = 0 22 | } 23 | 24 | 25 | def combineComputationsFold(f1: List[Fx[Double]]): Fx[Double] = 26 | f1.traverse(identity).map { lst => 27 | lst.foldLeft(0D) { (runningSum, next) => runningSum + next } } 28 | 29 | def combineComputations(f1: List[Fx[Double]]): Fx[Double] = 30 | f1.traverse(identity).map(_.combineAll) 31 | 32 | 33 | val samples: List[Fx[Double]] = 34 | (1 to 5).toList.map { x => Right(x.toDouble) } 35 | 36 | val samplesErr: List[Fx[Double]] = 37 | (1 to 5).toList.map { 38 | case x if x % 2 == 0 => Left(List(s"$x is not a multiple of 2")) 39 | case x => Right(x.toDouble) 40 | } 41 | 42 | println(combineComputationsFold(samples)) // Right(15.0) 43 | println(combineComputationsFold(samplesErr)) // Left(List(2 is not a multiple of 2, 4 is not a multiple of 2)) 44 | println(combineComputations(samples)) // Right(15.0) 45 | println(combineComputations(samplesErr)) // Left(List(2 is not a multiple of 2, 4 is not a multiple of 2)) 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Chapter06/jvm/src/main/scala/jvm/Bank.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object Bank { 4 | class Connection 5 | case class User(id: Option[Int], name: String) 6 | case class Account(id: Option[Int], ownerId: Int, balance: Double) 7 | 8 | def createUser(u: User, c: Connection): Int = ??? 9 | def createAccount(a: Account, c: Connection): Int = ??? 10 | 11 | def registerNewUser(name: String, c: Connection): Int = { 12 | val uid = createUser(User(None, name), c) 13 | val accId = createAccount(Account(None, uid, 0), c) 14 | accId 15 | } 16 | 17 | def createUserFunc (u: User ): Connection => Int = ??? 18 | def createAccountFunc(a: Account): Connection => Int = ??? 19 | 20 | def registerNewUserFunc(name: String): Connection => Int = { c: Connection => 21 | val uid = createUserFunc(User(None, name))(c) 22 | val accId = createAccountFunc(Account(None, uid, 0))(c) 23 | accId 24 | } 25 | 26 | case class Reader[A, B](f: A => B) { 27 | def apply(a: A): B = f(a) 28 | 29 | def flatMap[C](f2: B => Reader[A, C]): Reader[A, C] = 30 | Reader { a => f2(f(a))(a) } 31 | } 32 | 33 | def createUserReader (u: User ): Reader[Connection, Int] = Reader { _ => 0 } // Dummy implementation, always returns 0 34 | def createAccountReader(a: Account): Reader[Connection, Int] = Reader { _ => 1 } // Dummy implementation, always returns 0 35 | 36 | def registerNewUserReader(name: String): Reader[Connection, Int] = 37 | createUserReader(User(None, name)).flatMap { uid => 38 | createAccountReader(Account(None, uid, 0)) } 39 | 40 | def main(args: Array[String]): Unit = { 41 | val reader: Reader[Connection, Int] = registerNewUserReader("John") 42 | val accId = reader(new Connection) 43 | println(s"Success, account id: $accId") // Success, account id: 1 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Chapter13/build.sbt: -------------------------------------------------------------------------------- 1 | val ScalaVer = "2.12.1" 2 | 3 | val Cats = "0.9.0" 4 | val Shapeless = "2.3.2" 5 | val Scalacheck = "1.13.4" 6 | val KindProjector = "0.9.3" 7 | val FS2 = "0.9.4" 8 | val Matryoshka = "0.16.5" 9 | val Jsoup = "1.10.2" 10 | val Akka = "2.4.17" 11 | 12 | val ScalacheckMinTests = 1000 13 | 14 | lazy val commonSettings = Seq( 15 | name := "crawler" 16 | , version := "0.1.0" 17 | , scalaVersion := ScalaVer 18 | , libraryDependencies ++= Seq( 19 | "org.typelevel" %% "cats" % Cats 20 | , "com.chuusai" %% "shapeless" % Shapeless 21 | , "co.fs2" %% "fs2-core" % FS2 22 | , "co.fs2" %% "fs2-io" % FS2 23 | , "com.slamdata" %% "matryoshka-core" % Matryoshka 24 | , "org.scalacheck" %% "scalacheck" % Scalacheck % "test" 25 | , "org.jsoup" % "jsoup" % Jsoup 26 | 27 | , "com.typesafe.akka" %% "akka-actor" % Akka 28 | ) 29 | , addCompilerPlugin("org.spire-math" %% "kind-projector" % KindProjector) 30 | , scalacOptions ++= Seq( 31 | "-deprecation", 32 | "-encoding", "UTF-8", 33 | "-feature", 34 | "-language:existentials", 35 | "-language:higherKinds", 36 | "-language:implicitConversions", 37 | "-language:experimental.macros", 38 | "-unchecked", 39 | // "-Xfatal-warnings", 40 | "-Xlint", 41 | // "-Yinline-warnings", 42 | "-Ywarn-dead-code", 43 | "-Xfuture", 44 | "-Ypartial-unification") 45 | , testOptions in Test += Tests.Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", ScalacheckMinTests.toString, "-workers", "10", "-verbosity", "1") 46 | ) 47 | 48 | lazy val root = (project in file(".")) 49 | .settings(commonSettings) 50 | .settings( 51 | initialCommands := "import crawler._" 52 | ) 53 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/Server.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | 3 | import scala.concurrent.ExecutionContext.Implicits.global 4 | 5 | import cats._, cats.implicits._, cats.effect._ 6 | import org.http4s._, org.http4s.dsl.io._, org.http4s.server.blaze._ 7 | import io.circe.generic.auto._ 8 | 9 | 10 | object Server extends App with CirceImplicits { 11 | def noAuthCookieError = 12 | new RuntimeException("Please include the auth cookie") 13 | 14 | def all = ( 15 | createCustomer 16 | <+> placeOrder 17 | <+> listOrders 18 | <+> listGoods) 19 | 20 | def success[T](payload: T): Map[String, T] = 21 | Map("success" -> payload) 22 | 23 | def createCustomer = HttpService[IO] { 24 | case req @ POST -> Root / "customer" => 25 | for { 26 | reqBody <- req.as[Customer] 27 | id <- db.customer.create(reqBody) 28 | resp <- Ok(success(id.toString)) 29 | } yield resp 30 | } 31 | 32 | def placeOrder = HttpService[IO] { 33 | case req @ POST -> Root / "order" => 34 | for { 35 | cookieHeader <- 36 | headers.Cookie.from(req.headers).map(IO.pure).getOrElse( 37 | IO.raiseError(noAuthCookieError)) 38 | jsonBody <- req.as[Map[String, Int]] 39 | cookie <- cookieHeader.values.toList 40 | .find(_.name == "shop_customer_id").map(IO.pure).getOrElse( 41 | IO.raiseError(noAuthCookieError)) 42 | uId = cookie.content 43 | 44 | oId <- db.order.create(Order(good = jsonBody("good"), customer = uId.toInt)) 45 | order <- db.order.get(oId) 46 | resp <- Ok(success(order)) 47 | } yield resp 48 | } 49 | 50 | def listOrders = HttpService[IO] { 51 | case req @ GET -> Root / "order" => 52 | db.order.list.flatMap(Ok(_)) 53 | } 54 | 55 | def listGoods = HttpService[IO] { 56 | case req @ GET -> Root / "good" => 57 | db.good.list.flatMap(Ok(_)) 58 | } 59 | 60 | 61 | BlazeBuilder[IO] 62 | .bindHttp(8888, "0.0.0.0") 63 | .mountService(all, "/") 64 | .serve.compile.drain.unsafeRunSync() 65 | } 66 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/server/db/Customer.scala: -------------------------------------------------------------------------------- 1 | package jvm.server 2 | package db 3 | 4 | import cats._, cats.implicits._, cats.effect._, cats.data._ 5 | import doobie._, doobie.implicits._ 6 | 7 | import infrastructure.tr 8 | 9 | object CustomerDB extends App { 10 | val customersTest: IO[Unit] = for { 11 | id1 <- customer.create(Customer(name = "John Smith")) 12 | id2 <- customer.create(Customer(name = "Ann Watson")) 13 | 14 | _ = println(s"Looking up customers by name") 15 | c1 <- customer.findByName("John Smith") 16 | _ = println(c1) 17 | c2 <- customer.findByName("Foo") 18 | _ = println(c2) 19 | 20 | _ = println("\nAll customers") 21 | cs <- customer.list 22 | _ = println(cs.mkString("\n")) 23 | 24 | _ = println(s"\nCustomer with id $id1") 25 | c3 <- customer.get(id1) 26 | _ = println(c3) 27 | 28 | _ = println(s"\nUpdate customer with id $id1") 29 | r <- customer.update(c3.copy(name = "Bob")) 30 | _ = println(s"Rows affected: $r") 31 | c4 <- customer.get(id1) 32 | _ = println(s"Updated customer: $c4") 33 | 34 | _ = println(s"\nClean-up: remove all customers") 35 | _ <- List(id1, id2).traverse(customer.delete) 36 | cx <- customer.list 37 | _ = println(s"Customers table after clean-up: $cx") 38 | } yield () 39 | 40 | customersTest.unsafeRunSync() 41 | } 42 | 43 | object customer extends CustomerDbHelpers { 44 | def create(c: Customer): IO[Int] = 45 | sql""" 46 | insert into customer (name) 47 | values (${c.name}) 48 | """ 49 | .update.withUniqueGeneratedKeys[Int]("id").transact(tr) 50 | 51 | def findByName(name: String): IO[Option[Customer]] = 52 | (selectCustomerSql ++ sql"""where name = $name""") 53 | .query[Customer].option.transact(tr) 54 | 55 | def list: IO[List[Customer]] = 56 | selectCustomerSql.query[Customer].to[List].transact(tr) 57 | 58 | def get(id: Int): IO[Customer] = 59 | (selectCustomerSql ++ sql"where id = $id") 60 | .query[Customer].unique.transact(tr) 61 | 62 | def update(c: Customer): IO[Int] = 63 | sql""" 64 | update customer set 65 | name = ${c.name} 66 | where id = ${c.id} 67 | """ 68 | .update.run.transact(tr) 69 | 70 | def delete(id: Int): IO[Int] = 71 | sql"""delete from customer where id = $id""" 72 | .update.run.transact(tr) 73 | } 74 | 75 | trait CustomerDbHelpers { 76 | val selectCustomerSql = fr"select * from customer" 77 | } 78 | -------------------------------------------------------------------------------- /Chapter10/jvm/src/main/scala/jvm/TaglessFinal.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.concurrent.{ Future, Await } 4 | import scala.concurrent.ExecutionContext.Implicits.global 5 | import scala.concurrent.duration.Duration 6 | 7 | import cats._, cats.implicits._ 8 | 9 | trait Capabilities[F[_]] { 10 | def resource(name: String): F[String] 11 | def notify(target: String, text: String): F[Unit] 12 | } 13 | 14 | object TaglessFinalExample extends App { 15 | implicit val capabilities: Capabilities[Future] = new Capabilities[Future] { 16 | import java.io.File 17 | import org.apache.commons.io.FileUtils 18 | 19 | def resource(name: String): Future[String] = 20 | Future { FileUtils.readFileToString(new File(name), "utf8") } 21 | 22 | def notify(target: String, text: String): Future[Unit] = 23 | Future { println(s"Notifying $target: $text") } 24 | } 25 | 26 | implicit val anotherEnvironmentCapabilities: Capabilities[Future] = new Capabilities[Future] { 27 | def resource(name: String): Future[String] = ??? 28 | def notify(target: String, text: String): Future[Unit] = ??? 29 | } 30 | 31 | implicit val logMonad: Monad[Future] = new Monad[Future] { 32 | def flatMap[A, B](fa: Future[A])(f: (A) ⇒ Future[B]): Future[B] = 33 | fa.flatMap { x => 34 | println(s"Trace of the Future's result: $x") 35 | f(x) } 36 | 37 | def pure[A](x: A): Future[A] = Future(x) 38 | 39 | def tailRecM[A, B](a: A)(f: (A) ⇒ Future[Either[A, B]]): Future[B] = ??? 40 | } 41 | 42 | def income[F[_]](implicit M: Monad[F], C: Capabilities[F]): F[Unit] = 43 | for { 44 | contents <- C.resource("sales.csv") 45 | total = contents 46 | .split("\n").toList.tail // Collection of lines, drop the CSV header 47 | .map { _.split(",").toList match // List[Double] - prices of each of the entries 48 | { case name :: price :: Nil => price.toDouble } 49 | } 50 | .sum 51 | _ <- C.notify("admin@shop.com", s"Total income made today: $total") 52 | } yield () 53 | 54 | Await.result(income[Future](logMonad, capabilities), Duration.Inf) // Block so that the application does not exit prematurely 55 | } 56 | 57 | object FacadeExample { 58 | trait Capabilities { 59 | def resource(name: String): String 60 | def notify(target: String, text: String): Unit 61 | } 62 | 63 | def income(c: Capabilities): Unit = { 64 | val contents = c.resource("sales.csv") 65 | val total = contents 66 | .split("\n").toList.tail // Collection of lines, drop the CSV header 67 | .map { _.split(",").toList match // List[Double] - prices of each of the entries 68 | { case name :: price :: Nil => price.toDouble } 69 | } 70 | .sum 71 | c.notify("admin@shop.com", s"Total income made today: $total") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Chapter13/src/main/scala/crawler/ActorBased.scala: -------------------------------------------------------------------------------- 1 | package crawler 2 | 3 | import java.net.URL 4 | 5 | import scala.collection.JavaConverters._ 6 | 7 | import org.jsoup.nodes._ 8 | import org.jsoup._ 9 | 10 | import akka.actor._, akka.pattern._ 11 | import akka.util.Timeout 12 | import scala.concurrent.duration.SECONDS 13 | import scala.concurrent.ExecutionContext.Implicits.global 14 | 15 | import Main.fetch 16 | import Protocol._ 17 | 18 | object ActorBased { 19 | implicit val timeout: Timeout = Timeout(30, SECONDS) 20 | 21 | def main(args: Array[String]): Unit = { 22 | val system = ActorSystem("PiSystem") 23 | val root = system actorOf Worker.workerProps 24 | 25 | (root ? Job(new URL("http://mvnrepository.com/"), 1)).onSuccess { 26 | case Result(res) => 27 | println("Crawling finished successfully") 28 | println(res.take(10).mkString("\n")) 29 | println(res.size) 30 | 31 | } 32 | } 33 | } 34 | 35 | class Worker extends Actor { 36 | var buffer : Set[URL] = Set() 37 | var children: Set[ActorRef] = Set() 38 | var replyTo : Option[ActorRef] = None 39 | 40 | var answered = 0 41 | 42 | def receive = awaitingForTasks 43 | 44 | def dispatch(lnk: URL, depth: Int, visited: Set[URL]): Unit = { 45 | val child = context actorOf Worker.workerProps 46 | children += child 47 | child ! Job(lnk, depth, visited) 48 | } 49 | 50 | def awaitingForTasks: Receive = { 51 | case Job(url, depth, visited) => 52 | replyTo = Some(sender) 53 | 54 | val links = fetch(url).getOrElse(Set()).filter(!visited(_)) 55 | buffer = links 56 | 57 | if (depth > 0) { 58 | println(s"Processing links of $url, descending now") 59 | 60 | children = Set() 61 | answered = 0 62 | 63 | for { l <- links } dispatch(l, depth - 1, visited) 64 | context become processing 65 | } 66 | else { 67 | println(s"Reached maximal depth on $url - returning its links only") 68 | sender ! Result(buffer) 69 | context stop self 70 | } 71 | } 72 | 73 | 74 | def processing: Receive = { 75 | case Result(urls) => 76 | replyTo match { 77 | case Some(to) => 78 | answered += 1 79 | println(s"$self: $answered actors responded of ${children.size}") 80 | buffer ++= urls 81 | if (answered == children.size) { 82 | to ! Result(buffer) 83 | context stop self 84 | } 85 | 86 | case None => println("replyTo actor is None, something went wrong") 87 | } 88 | } 89 | } 90 | 91 | object Worker { 92 | def workerProps: Props = Props(classOf[Worker]) 93 | } 94 | 95 | 96 | object Protocol { 97 | sealed trait Msg 98 | case class Job(url : URL, depth: Int, visited: Set[URL] = Set()) extends Msg 99 | case class Result (urls: Set[URL]) extends Msg 100 | } -------------------------------------------------------------------------------- /Chapter10/jvm/src/main/scala/jvm/HttpServer.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.language.reflectiveCalls 4 | 5 | import scala.concurrent.Future 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | 8 | import cats._, cats.implicits._, cats.data._ 9 | 10 | 11 | trait HttpServer { 12 | type Token 13 | type Request <: { def token: Token } 14 | type Response 15 | type Post 16 | 17 | def allPosts(): List[Post] 18 | 19 | def respond[A](a: A): Response 20 | 21 | def authenticate(token: Token): Boolean 22 | 23 | def handle(request: Request): Response = { 24 | val userToken: Token = request.token 25 | val authenticated: Boolean = authenticate(userToken) 26 | 27 | if (authenticated) { 28 | val posts: List[Post] = allPosts() 29 | respond(posts) 30 | } 31 | else respond("You are not authorized to perform this action") 32 | } 33 | } 34 | 35 | trait HttpServerAsync { 36 | type Token 37 | type Request <: { def token: Token } 38 | type Response 39 | type Post 40 | 41 | def allPosts(): Future[List[Post]] 42 | 43 | def respond[A](a: A): Response 44 | 45 | def authenticate(token: Token): Future[Boolean] 46 | 47 | def handle(request: Request): Future[Response] = 48 | for { 49 | authenticated <- authenticate(request.token) 50 | response <- 51 | if (authenticated) allPosts.map(respond) 52 | else Future { respond("You are not authorized to perform this action") } 53 | } yield response 54 | } 55 | 56 | trait HttpServerStackingNaive { 57 | type Token 58 | type Request <: { def token: Token } 59 | type Response 60 | type Post 61 | 62 | def allPosts(): Future[Either[String, List[Post]]] 63 | 64 | def respond[A](a: A): Response 65 | 66 | def authenticate(token: Token): Future[Either[String, Boolean]] 67 | 68 | def handle(request: Request): Future[Either[String, Response]] = 69 | authenticate(request.token).flatMap { 70 | case Right(authenticated) if authenticated => 71 | allPosts.map { eitherPosts => 72 | eitherPosts.map(respond) 73 | } 74 | 75 | case Right(authenticated) if !authenticated => 76 | Future { Left("You are not authorized to perform this action") } 77 | 78 | case Left(error) => Future(Left(error)) 79 | } 80 | } 81 | 82 | trait HttpServerTransformers { 83 | type Token 84 | type Request <: { def token: Token } 85 | type Response 86 | type Post 87 | 88 | type Config 89 | type Ef[A] = ReaderT[EitherT[Future, String, ?], Config, A] 90 | 91 | def allPosts(): EitherT[Future, String, List[Post]] 92 | 93 | def respond[A](a: A): Response 94 | 95 | def authenticate(token: Token): EitherT[Future, String, Boolean] 96 | 97 | def handle(request: Request): EitherT[Future, String, Response] = 98 | for { 99 | authenticated <- authenticate(request.token) 100 | .ensure("You are not authorized to perform this action")(identity) 101 | posts <- allPosts() 102 | response = respond(posts) 103 | } yield response 104 | } 105 | -------------------------------------------------------------------------------- /Chapter10/jvm/src/main/scala/jvm/HListSum.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | case class Fraction(numerator: Int, denominator: Int) 4 | 5 | object ListSum extends App { 6 | val list: List[Any] = List(0, 2.0, "3", Fraction(4, 2)) 7 | val sum = list.map { 8 | case x: Int => x.toDouble 9 | case x: Double => x 10 | case x: String => x.toDouble 11 | // case Fraction(n, d) => n / d.toDouble 12 | }.sum 13 | println(sum) 14 | } 15 | 16 | object HListSum extends App { 17 | def sumSimple[L <: HList](hlist: L)(implicit m: MapToDouble[L]): Double = { 18 | val mapped: m.Result = m.map(hlist) 19 | def loop(l: HList): Double = l match { 20 | case :::(h: Double, t) => h + loop(t) 21 | case HNil => 0 22 | } 23 | loop(mapped) 24 | } 25 | 26 | def sum[L <: HList, LR <: HList](hlist: L)(implicit m: MapToDouble.Aux[L, LR], s: Sum[LR]): Double = 27 | s.sum(m.map(hlist)) 28 | 29 | val hlist: String ::: Int ::: Fraction ::: HNil = 30 | "1" ::: 2 ::: Fraction(3, 4) ::: HNil 31 | 32 | val s = sum(hlist) 33 | println(s"Sum of $hlist is $s") 34 | } 35 | 36 | sealed trait HList { 37 | def :::[H](h: H): H ::: this.type = jvm.:::(h, this) 38 | } 39 | case class :::[+H, +T <: HList](head: H, tail: T) extends HList { 40 | override def toString() = s"$head ::: $tail" 41 | } 42 | trait HNil extends HList 43 | case object HNil extends HNil 44 | 45 | trait MapToDouble[L <: HList] { 46 | type Result <: HList 47 | def map(l: L): Result 48 | } 49 | 50 | trait MapToDoubleNoAux[L <: HList, Result <: HList] { 51 | def map(l: L): Result 52 | } 53 | 54 | 55 | trait Sum[L] { 56 | def sum(l: L): Double 57 | } 58 | 59 | trait ToDouble[T] { 60 | def toDouble(t: T): Double 61 | } 62 | 63 | object MapToDouble { 64 | type Aux[L <: HList, LR <: HList] = MapToDouble[L] { type Result = LR } 65 | def apply[L <: HList](implicit m: MapToDouble[L]) = m 66 | 67 | implicit def hcons[H, T <: HList, TR <: HList](implicit 68 | td: ToDouble[H] 69 | , md: MapToDouble.Aux[T, TR] 70 | ): Aux[H ::: T, Double ::: TR] = new MapToDouble[H ::: T] { 71 | 72 | type Result = Double ::: TR 73 | def map(l: H ::: T): Double ::: TR = 74 | td.toDouble(l.head) ::: md.map(l.tail) 75 | } 76 | 77 | implicit def hnil[H <: HNil]: MapToDouble.Aux[H, HNil] = new MapToDouble[H] { 78 | 79 | type Result = HNil 80 | def map(h: H) = HNil 81 | } 82 | } 83 | 84 | object Sum { 85 | def apply[L <: HList](implicit s: Sum[L]) = s 86 | 87 | implicit def hcons[T <: HList](implicit st: Sum[T]): Sum[Double ::: T] = 88 | { (l: Double ::: T) => l.head + st.sum(l.tail) } 89 | 90 | implicit def hnil[H <: HNil]: Sum[H] = 91 | { (x: HNil) => 0 } 92 | } 93 | 94 | object ToDouble { 95 | def apply[T](implicit t: ToDouble[T]) = t 96 | 97 | implicit def double: ToDouble[Double] = identity 98 | implicit def int : ToDouble[Int ] = _.toDouble 99 | implicit def string: ToDouble[String] = _.toDouble 100 | 101 | implicit def fraction: ToDouble[Fraction] = 102 | f => f.numerator / f.denominator.toDouble 103 | } 104 | -------------------------------------------------------------------------------- /Chapter06/jvm/src/main/scala/jvm/Weather.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.concurrent.{ Future, ExecutionContext } 4 | import java.util.concurrent.Executors 5 | 6 | 7 | object Weather { 8 | case class Event(time: Long, location: String) 9 | 10 | def getEvent(id: Int): Event = { 11 | Thread.sleep(1000) // Simulate delay 12 | Event(System.currentTimeMillis, "New York") 13 | } 14 | 15 | def getWeather(time: Long, location: String): String = { 16 | Thread.sleep(1000) // Simulate delay 17 | "bad" 18 | } 19 | 20 | def notifyUser(): Unit = { 21 | Thread.sleep(1000) 22 | println("The user is notified") 23 | } 24 | 25 | def weatherImperative(eventId: Int): Unit = { 26 | val evt = getEvent(eventId) // Will block 27 | val weather = getWeather(evt.time, evt.location) // Will block 28 | if (weather == "bad") notifyUser() // Will block 29 | } 30 | 31 | def weatherImperativeThreaded(eventId: Int): Unit = { 32 | // Utility methods 33 | def thread(op: => Unit): Thread = 34 | new Thread(new Runnable { def run(): Unit = { op }}) 35 | 36 | def runThread(t: Thread): Unit = t.start() 37 | 38 | 39 | // Business logic methods 40 | def notifyThread(weather: String): Thread = thread { 41 | if (weather == "bad") notifyUser() 42 | } 43 | 44 | def weatherThread(evt: Event): Thread = thread { 45 | val weather = getWeather(evt.time, evt.location) 46 | runThread(notifyThread(weather)) 47 | } 48 | 49 | val eventThread: Thread = thread { 50 | val evt = getEvent(eventId) 51 | runThread(weatherThread(evt)) 52 | } 53 | 54 | 55 | // Run the app 56 | runThread(eventThread) // Prints "The user is notified" 57 | } 58 | 59 | def weatherFuture(eventId: Int): Unit = { 60 | implicit val context = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(5)) 61 | 62 | Future { getEvent(eventId) } 63 | .onSuccess { case evt => 64 | Future { getWeather(evt.time, evt.location) } 65 | .onSuccess { case weather => Future { if (weather == "bad") notifyUser } } 66 | } 67 | } 68 | 69 | def weatherFutureFlatmap(eventId: Int): Future[Unit] = { 70 | implicit val context = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(5)) 71 | 72 | for { 73 | evt <- Future { getEvent(eventId) } 74 | weather <- Future { getWeather(evt.time, evt.location) } 75 | _ <- Future { if (weather == "bad") notifyUser() } 76 | } yield () 77 | } 78 | 79 | def weatherFutureFlatmapDesugared(eventId: Int): Future[Unit] = { 80 | implicit val context = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(5)) 81 | 82 | Future { getEvent(eventId) } 83 | .flatMap { evt => Future { getWeather(evt.time, evt.location) } } 84 | .flatMap { weather => Future { if (weather == "bad") notifyUser() } } 85 | } 86 | 87 | } 88 | 89 | import Weather._ 90 | object WeatherImperativeSync extends App { weatherImperative(0) } 91 | object WeatherImperativeThreaded extends App { weatherImperativeThreaded(0) } 92 | object WeatherFuture extends App { weatherFuture(0) } 93 | object WeatherFutureFlatmap extends App { weatherFutureFlatmap(0) } 94 | object WeatherFutureFlatmapDesugared extends App { weatherFutureFlatmapDesugared(0) } 95 | -------------------------------------------------------------------------------- /Chapter11/jvm/src/main/scala/jvm/Deadlock.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.collection.JavaConverters._ 4 | 5 | import java.io.File 6 | import java.nio.charset.Charset 7 | 8 | import org.apache.commons.io.FileUtils 9 | 10 | object Deadlock { 11 | def main(args: Array[String]): Unit = { 12 | // Files we will be working with 13 | val input = new File("goods.csv" ) 14 | val log = new File("log.txt" ) 15 | val output = new File("goods.json") 16 | 17 | // Encoding for the file I/O operations 18 | val encoding = "utf8" 19 | 20 | // Convenience method to construct threads 21 | def makeThread(f: => Unit): Thread = 22 | new Thread(new Runnable { 23 | override def run(): Unit = f 24 | }) 25 | 26 | // Convenience method to write log 27 | def doLog(l: String): Unit = { 28 | FileUtils.write( 29 | log 30 | , l + "\n" 31 | , encoding 32 | , true // Append to the file rather than rewrite it 33 | ) 34 | println(s"Log: $l") // Trace console output 35 | } 36 | 37 | // Convenience method to read the input file 38 | def readInput(): List[(String, Int)] = 39 | FileUtils.readLines(input, encoding).asScala.toList.tail 40 | .map(_.split(',').toList match { 41 | case name :: price :: Nil => (name, price.toInt) 42 | }) 43 | 44 | val csv2json: Thread = makeThread { 45 | val inputList: List[(String, Int)] = 46 | input.synchronized { 47 | val result = readInput() 48 | log.synchronized { 49 | doLog(s"Read ${result.length} lines from input") 50 | } 51 | result 52 | } 53 | 54 | val json: List[String] = 55 | inputList.map { case (name, price) => 56 | s"""{"Name": "$name", "Price": $price}""" } 57 | 58 | FileUtils.writeLines(output, json.asJava) 59 | } 60 | 61 | def statistics(avg: Boolean = true, max: Boolean = false, min: Boolean = false): Thread = makeThread { 62 | val inputList: List[(String, Int)] = log.synchronized { 63 | doLog(s"Computing the following stats: avg=$avg, max=$max, min=$min") 64 | val res = input.synchronized { readInput() } 65 | doLog(s"Read the input file to compute statistics on it") 66 | res 67 | } 68 | 69 | val prices: List[Int] = inputList.map(_._2) 70 | def reportMetrics(name: String, value: => Double): Unit = { 71 | val result = value 72 | log.synchronized { doLog(s"$name: $result") } 73 | } 74 | 75 | if (avg) reportMetrics("Average Price", prices.sum / prices.length.toDouble) 76 | if (max) reportMetrics("Maximal Price", prices.max) 77 | if (min) reportMetrics("Minimal Price", prices.min) 78 | } 79 | 80 | def statisticsSafe(avg: Boolean = true, max: Boolean = false, min: Boolean = false): Thread = makeThread { 81 | val inputList: List[(String, Int)] = input.synchronized { 82 | log.synchronized { 83 | doLog(s"Computing the following stats: avg=$avg, max=$max, min=$min") 84 | val res = readInput() 85 | doLog(s"Read the input file to compute statistics on it") 86 | res 87 | } 88 | } 89 | 90 | val prices: List[Int] = inputList.map(_._2) 91 | def reportMetrics(name: String, value: => Double): Unit = { 92 | val result = value 93 | log.synchronized { doLog(s"$name: $result") } 94 | } 95 | 96 | if (avg) reportMetrics("Average Price", prices.sum / prices.length.toDouble) 97 | if (max) reportMetrics("Maximal Price", prices.max) 98 | if (min) reportMetrics("Minimal Price", prices.min) 99 | } 100 | 101 | csv2json.start() 102 | statisticsSafe(true, true, true).start() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Chapter07/jvm/src/main/scala/jvm/Logging.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | object LoggingWriter extends App { 4 | import SimpleWriter.log 5 | 6 | def add(a: Double, b: Double): SimpleWriter[Double] = 7 | for { 8 | _ <- log(s"Adding $a to $b") 9 | res = a + b 10 | _ <- log(s"The result of the operation is $res") 11 | } yield res 12 | 13 | println(add(1, 2)) // SimpleWriter(List(Adding 1.0 to 2.0, The result of the operation is 3.0),3.0) 14 | } 15 | 16 | object LoggingIO extends App { 17 | import IO.log 18 | 19 | def addIO(a: Double, b: Double): IO[Double] = 20 | for { 21 | _ <- log(s"Adding $a to $b") 22 | res = a + b 23 | _ <- log(s"The result of the operation is $res") 24 | } yield res 25 | 26 | addIO(1, 2).operation() 27 | // Outputs: 28 | // Writing message to log file: Adding 1.0 to 2.0 29 | // Writing message to log file: The result of the operation is 3.0 30 | } 31 | 32 | object LoggingAbstract extends App { 33 | // Does not compile 34 | // def add[F[_]](a: Double, b: Double): F[Double] = 35 | // for { 36 | // _ <- log(s"Adding $a to $b") 37 | // res = a + b 38 | // _ <- log(s"The result of the operation is $res") 39 | // } yield res 40 | 41 | import Monad.Ops 42 | 43 | def add[F[_]](a: Double, b: Double)(implicit M: Monad[F], L: Logging[F]): F[Double] = 44 | for { 45 | _ <- L.log(s"Adding $a to $b") 46 | res = a + b 47 | _ <- L.log(s"The result of the operation is $res") 48 | } yield res 49 | 50 | println(add[SimpleWriter](1, 2)) // SimpleWriter(List(Adding 1.0 to 2.0, The result of the operation is 3.0),3.0) 51 | 52 | println(add[IO](1, 2).operation()) 53 | // Outputs: 54 | // Writing message to log file: Adding 1.0 to 2.0 55 | // Writing message to log file: The result of the operation is 3.0 56 | // 3.0 57 | } 58 | 59 | trait Monad[F[_]] { 60 | def pure[A](a: A): F[A] 61 | def map[A, B](fa: F[A])(f: A => B): F[B] 62 | def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] 63 | } 64 | 65 | object Monad { 66 | implicit class Ops[F[_], A](fa: F[A])(implicit m: Monad[F]) { 67 | def map[B](f: A => B): F[B] = m.map(fa)(f) 68 | def flatMap[B](f: A => F[B]): F[B] = m.flatMap(fa)(f) 69 | } 70 | 71 | implicit val writerMonad: Monad[SimpleWriter] = new Monad[SimpleWriter] { 72 | def pure[A](a: A): SimpleWriter[A] = 73 | SimpleWriter.pure(a) 74 | 75 | def map[A, B](fa: SimpleWriter[A])(f: A => B): SimpleWriter[B] = 76 | fa.map(f) 77 | 78 | def flatMap[A, B](fa: SimpleWriter[A])(f: A => SimpleWriter[B]): SimpleWriter[B] = 79 | fa.flatMap(f) 80 | } 81 | 82 | implicit val ioMonad: Monad[IO] = new Monad[IO] { 83 | def pure[A](a: A): IO[A] = 84 | IO.suspend(a) 85 | 86 | def map[A, B](fa: IO[A])(f: A => B): IO[B] = 87 | fa.map(f) 88 | 89 | def flatMap[A, B](fa: IO[A])(f: A => IO[B]): IO[B] = 90 | fa.flatMap(f) 91 | } 92 | } 93 | 94 | trait Logging[F[_]] { 95 | def log(msg: String): F[Unit] 96 | } 97 | 98 | object Logging { 99 | implicit val writerLogging: Logging[SimpleWriter] = new Logging[SimpleWriter] { 100 | def log(msg: String) = SimpleWriter.log(msg) 101 | } 102 | 103 | implicit val ioLogging: Logging[IO] = new Logging[IO] { 104 | def log(msg: String) = IO.log(msg) 105 | } 106 | } 107 | 108 | 109 | case class IO[A](operation: () => A) { 110 | def flatMap[B](f: A => IO[B]): IO[B] = 111 | IO.suspend { f(operation()).operation() } 112 | 113 | def map[B](f: A => B): IO[B] = 114 | IO.suspend { f(operation()) } 115 | } 116 | 117 | object IO { 118 | def suspend[A](op: => A): IO[A] = IO(() => op) 119 | 120 | def log(str: String): IO[Unit] = 121 | IO.suspend { println(s"Writing message to log file: $str") } 122 | } 123 | 124 | case class SimpleWriter[A](log: List[String], value: A) { 125 | def flatMap[B](f: A => SimpleWriter[B]): SimpleWriter[B] = { 126 | val wb: SimpleWriter[B] = f(value) 127 | SimpleWriter(log ++ wb.log, wb.value) 128 | } 129 | 130 | def map[B](f: A => B): SimpleWriter[B] = 131 | SimpleWriter(log, f(value)) 132 | } 133 | 134 | object SimpleWriter { 135 | // Wraps a value into SimpleWriter 136 | def pure[A](value: A): SimpleWriter[A] = 137 | SimpleWriter(Nil, value) 138 | 139 | // Wraps a log message into SimpleWriter 140 | def log(message: String): SimpleWriter[Unit] = 141 | SimpleWriter(List(message), ()) 142 | } 143 | -------------------------------------------------------------------------------- /Chapter09/jvm/src/main/scala/jvm/Concurrency.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.language.postfixOps 4 | import cats._, cats.implicits._, cats.effect._, cats.data._ 5 | 6 | import scala.concurrent.ExecutionContext 7 | import java.util.concurrent.Executors 8 | import scala.concurrent.duration._ 9 | 10 | object HelloWorld extends App { 11 | val hello = IO { println("Hello") } 12 | val world = IO { println("World") } 13 | (hello *> world).unsafeRunSync 14 | } 15 | 16 | trait Context { 17 | implicit val ec: ExecutionContext = 18 | ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2)) 19 | 20 | def benchmark[A](io: IO[A]): IO[(A, Long)] = 21 | for { 22 | tStart <- Timer[IO].clockMonotonic(SECONDS) 23 | res <- io 24 | tEnd <- Timer[IO].clockMonotonic(SECONDS) 25 | } yield (res, tEnd - tStart) 26 | 27 | def benchmarkFlush[A](io: IO[A]): IO[Unit] = 28 | benchmark(io).map { case (res, time) => 29 | println(s"Computed result $res in $time seconds") } 30 | } 31 | 32 | trait Asynchrony extends Context { 33 | def taskHeavy(prefix: String): IO[Nothing] = 34 | Monad[IO].tailRecM(0) { i => for { 35 | _ <- IO { println(s"${Thread.currentThread.getName}; $prefix: $i") } 36 | _ <- IO { Thread.sleep(1000) } 37 | } yield Left(i + 1) } 38 | 39 | def taskLight(prefix: String): IO[Nothing] = 40 | Monad[IO].tailRecM(0) { i => for { 41 | _ <- IO { println(s"${Thread.currentThread.getName}; $prefix: $i") } 42 | _ <- IO.sleep(1 second) 43 | } yield Left(i + 1) } 44 | 45 | def bunch(n: Int)(gen: String => IO[Nothing]): IO[List[Fiber[IO, Nothing]]] = 46 | (1 to n).toList.map(i => s"Task $i").traverse(gen(_).start) 47 | } 48 | 49 | object AsynchronyHeavy extends Asynchrony with App { 50 | (IO.shift *> bunch(1000)(taskHeavy)).unsafeRunSync } 51 | 52 | object AsynchronyLight extends Asynchrony with App { 53 | (IO.shift *> bunch(1000)(taskLight)).unsafeRunSync } 54 | 55 | trait Fibers extends Context { 56 | def sum(from: Int, to: Int): IO[Int] = 57 | Monad[IO].tailRecM((from, 0)) { case (i, runningTotal) => 58 | if (i == to) IO.pure( Right(runningTotal + i) ) 59 | else if (i > to) IO.pure( Right(runningTotal) ) 60 | else for { 61 | _ <- IO { println(s"${Thread.currentThread.getName}: " + 62 | s"Running total from $from to $to, currently at $i: $runningTotal") } 63 | _ <- IO.sleep(500 milliseconds) 64 | } yield Left((i + 1, runningTotal + i)) } 65 | 66 | def sequential: IO[Int] = 67 | for { 68 | s1 <- sum(1 , 10) 69 | s2 <- sum(10, 20) 70 | } yield s1 + s2 71 | 72 | def sequentialTraverse: IO[Int] = 73 | List(sum(1, 10), sum(10, 20)).traverse(identity).map(_.sum) 74 | 75 | def parallel: IO[Int] = 76 | for { 77 | f1 <- sum(1 , 10).start 78 | f2 <- sum(10, 20).start 79 | s1 <- f1.join 80 | s2 <- f2.join 81 | } yield s1 + s2 82 | 83 | def cancelled: IO[Int] = 84 | for { 85 | f1 <- sum(1 , 5 ).start 86 | f2 <- sum(10, 20).start 87 | res <- f1.join 88 | _ <- f2.cancel 89 | } yield res 90 | } 91 | 92 | object FibersSequantial extends Fibers with App { 93 | benchmarkFlush(sequential).unsafeRunSync } 94 | 95 | object SequentialTraverse extends Fibers with App { 96 | benchmarkFlush(sequentialTraverse).unsafeRunSync } 97 | 98 | object FibersParallel extends Fibers with App { 99 | benchmarkFlush(parallel).unsafeRunSync } 100 | 101 | object FibersCancelled extends Fibers with App { 102 | benchmarkFlush(cancelled).unsafeRunSync } 103 | 104 | 105 | trait SyncVsAsync extends Context { 106 | def taskHeavy(name: String): Int = { 107 | Thread.sleep(1000) 108 | println(s"${Thread.currentThread.getName}: " + 109 | s"$name: Computed!") 110 | 42 111 | } 112 | 113 | def sync(name: String): IO[Int] = 114 | IO { taskHeavy(name) } 115 | 116 | def async(name: String): IO[Int] = 117 | IO.async { cb => 118 | new Thread(new Runnable { override def run = 119 | cb { Right(taskHeavy(name)) } }).start() 120 | } 121 | 122 | def bunch(n: Int)(gen: String => IO[Int]): IO[List[Int]] = 123 | (1 to n).toList.map(i => s"Task $i").traverse(gen(_).start) 124 | .flatMap(_.traverse(_.join)) 125 | } 126 | 127 | object SyncVsAsyncSync extends SyncVsAsync with App { 128 | benchmarkFlush(IO.shift *> bunch(10)(sync)).unsafeRunSync } 129 | 130 | object SyncVsAsyncAsync extends SyncVsAsync with App { 131 | benchmarkFlush(IO.shift *> bunch(10)(async)).unsafeRunSync } 132 | -------------------------------------------------------------------------------- /Chapter08/jvm/src/main/scala/jvm/IndependentComputations.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import cats._, cats.syntax.all._ 4 | 5 | object IndependentComputations extends App { 6 | import cats.instances.all._ 7 | 8 | type Fx[A] = Either[List[String], A] 9 | 10 | def combineComputations(f1: Fx[Double], f2: Fx[Double]): Fx[Double] = 11 | for { 12 | r1 <- f1 13 | r2 <- f2 14 | } yield r1 + r2 15 | 16 | val result = combineComputations(Right(1.0), Right(2.0)) 17 | println(result) // Right(3.0) 18 | 19 | val resultFirstFailed = combineComputations( 20 | Left(List("Division by zero")), Right(2.0)) 21 | println(resultFirstFailed) // Left(List(Division by zero)) 22 | 23 | val resultSecondFailed = combineComputations( 24 | Right(1.0), Left(List("Null pointer encountered"))) 25 | println(resultSecondFailed) // Left(List(Null pointer encountered)) 26 | 27 | val resultBothFailed = combineComputations( 28 | Left(List("Division by zero")), Left(List("Null pointer encountered"))) 29 | println(resultBothFailed) // Left(List(Division by zero)) 30 | } 31 | 32 | object IndependentComputationsZip extends App { 33 | import cats.instances.all._ 34 | 35 | type Fx[A] = Either[List[String], A] 36 | 37 | def zip[A, B](f1: Fx[A], f2: Fx[B]): Fx[(A, B)] = (f1, f2) match { 38 | case (Right(r1), Right(r2)) => Right((r1, r2)) 39 | case (Left(e1), Left(e2)) => Left(e1 ++ e2) 40 | case (Left(e), _) => Left(e) 41 | case (_, Left(e)) => Left(e) 42 | } 43 | 44 | def combineComputations(f1: Fx[Double], f2: Fx[Double]): Fx[Double] = 45 | zip(f1, f2).map { case (r1, r2) => r1 + r2 } 46 | 47 | val result = combineComputations(Right(1.0), Right(2.0)) 48 | println(result) // Right(3.0) 49 | 50 | val resultFirstFailed = combineComputations( 51 | Left(List("Division by zero")), Right(2.0)) 52 | println(resultFirstFailed) // Left(List(Division by zero)) 53 | 54 | val resultSecondFailed = combineComputations( 55 | Right(1.0), Left(List("Null pointer encountered"))) 56 | println(resultSecondFailed) // Left(List(Null pointer encountered)) 57 | 58 | val resultBothFailed = combineComputations( 59 | Left(List("Division by zero")), Left(List("Null pointer encountered"))) 60 | println(resultBothFailed) // Left(List(Division by zero, Null pointer encountered)) 61 | } 62 | 63 | object IndependentComputationsAp extends App { 64 | import cats.instances.all._ 65 | 66 | type Fx[A] = Either[List[String], A] 67 | 68 | def ap[A, B](ff: Fx[A => B])(fa: Fx[A]): Fx[B] = (ff, fa) match { 69 | case (Right(f), Right(a)) => Right(f(a)) 70 | case (Left(e1), Left(e2)) => Left(e1 ++ e2) 71 | case (Left(e), _) => Left(e) 72 | case (_, Left(e)) => Left(e) 73 | } 74 | 75 | def zip[A, B](f1: Fx[A], f2: Fx[B]): Fx[(A, B)] = 76 | ap[B, (A, B)](ap[A, B => (A, B)](Right { (a: A) => (b: B) => (a, b) })(f1))(f2) 77 | 78 | def combineComputations(f1: Fx[Double], f2: Fx[Double]): Fx[Double] = 79 | zip(f1, f2).map { case (r1, r2) => r1 + r2 } 80 | 81 | val result = combineComputations(Right(1.0), Right(2.0)) 82 | println(result) // Right(3.0) 83 | 84 | val resultFirstFailed = combineComputations( 85 | Left(List("Division by zero")), Right(2.0)) 86 | println(resultFirstFailed) // Left(List(Division by zero)) 87 | 88 | val resultSecondFailed = combineComputations( 89 | Right(1.0), Left(List("Null pointer encountered"))) 90 | println(resultSecondFailed) // Left(List(Null pointer encountered)) 91 | 92 | val resultBothFailed = combineComputations( 93 | Left(List("Division by zero")), Left(List("Null pointer encountered"))) 94 | println(resultBothFailed) // Left(List(Division by zero, Null pointer encountered)) 95 | } 96 | 97 | object IndependentComputationsApplicative extends App { 98 | type Fx[A] = Either[List[String], A] 99 | 100 | implicit val applicative: Applicative[Fx] = new Applicative[Fx] { 101 | override def ap[A, B](ff: Fx[A => B])(fa: Fx[A]): Fx[B] = (ff, fa) match { 102 | case (Right(f), Right(a)) => Right(f(a)) 103 | case (Left(e1), Left(e2)) => Left(e1 ++ e2) 104 | case (Left(e), _) => Left(e) 105 | case (_, Left(e)) => Left(e) 106 | } 107 | 108 | override def pure[A](a: A): Fx[A] = Right(a) 109 | } 110 | 111 | def combineComputations(f1: Fx[Double], f2: Fx[Double]): Fx[Double] = 112 | (f1, f2).mapN { case (r1, r2) => r1 + r2 } 113 | 114 | val result = combineComputations(Right(1.0), Right(2.0)) 115 | println(result) // Right(3.0) 116 | 117 | val resultFirstFailed = combineComputations( 118 | Left(List("Division by zero")), Right(2.0)) 119 | println(resultFirstFailed) // Left(List(Division by zero)) 120 | 121 | val resultSecondFailed = combineComputations( 122 | Right(1.0), Left(List("Null pointer encountered"))) 123 | println(resultSecondFailed) // Left(List(Null pointer encountered)) 124 | 125 | val resultBothFailed = combineComputations( 126 | Left(List("Division by zero")), Left(List("Null pointer encountered"))) 127 | println(resultBothFailed) // Left(List(Division by zero, Null pointer encountered)) 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Mastering Functional Programming 5 | 6 | Mastering Functional Programming 7 | 8 | This is the code repository for [Mastering Functional Programming](https://www.packtpub.com/application-development/mastering-functional-programming?utm_source=github&utm_medium=repository&utm_campaign=9781788620796 ), published by Packt. 9 | 10 | **Functional techniques for sequential and parallel programming with Scala** 11 | 12 | ## What is this book about? 13 | In large projects, it can get difficult keeping track of all the interdependencies of the code base and how its state changes at runtime. Functional Programming helps us solve these problems. It is a paradigm specifically designed to deal with the complexity of software development. This book will show you how the right abstractions can reduce complexity and make your code easy to read and understand. 14 | 15 | This book covers the following exciting features: 16 | Write reliable and scalable software based on solid foundations 17 | Explore the cutting edge of computer science research 18 | Effectively solve complex architectural problems in a robust way 19 | Avoid unwanted outcomes such as errors or delays and focus on business logic 20 | Write parallel programs in a functional style using the actor model 21 | Use functional data structures and collections in your day-to-day work 22 | 23 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1788620798) today! 24 | 25 | https://www.packtpub.com/ 27 | 28 | ## Instructions and Navigations 29 | All of the code is organized into folders. For example, Chapter02. 30 | 31 | The code will look like the following: 32 | ``` 33 | public void drink() { 34 | System.out.println("You have drunk a can of soda."); 35 | } 36 | ``` 37 | 38 | **Following is what you need for this book:** 39 | If you are from an imperative and OOP background, this book will guide you through the world of functional programming, irrespective of which programming language you use. 40 | 41 | With the following software and hardware list you can run all code files present in the book (Chapter 1-15). 42 | ### Software and Hardware List 43 | | Chapter | Software required | OS required | 44 | | -------- | ----------------------------------------------------------------| -----------------------------------| 45 | | 1-14 | Docker version 18.06 or higher and Git version 2.18.0 or higher | Windows, macOS, and Linux (Any) | 46 | 47 | 48 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book. [Click here to download it](https://www.packtpub.com/sites/default/files/downloads/MasteringFunctionalProgramming_ColorImages.pdf). 49 | 50 | ### Related products 51 | * Scala Reactive Programming [[Packt]](https://www.packtpub.com/application-development/scala-reactive-programming?utm_source=github&utm_medium=repository&utm_campaign=) [[Amazon]](https://www.amazon.com/dp/B073FR5T8F) 52 | 53 | * Modern Scala Projects [[Packt]](https://www.packtpub.com/application-development/modern-scala-projects?utm_source=github&utm_medium=repository&utm_campaign=) [[Amazon]](https://www.amazon.com/dp/B07FDBHZ7V) 54 | 55 | 56 | 57 | ## Get to Know the Author 58 | **Anatolii Kmetiuk** 59 | is a Functional Programming and Data Science Freelance Developer. During his programming career, he has worked on Scala projects involving parallel computing, web APIs, SaaS, and data engineering. His areas of expertise include using applications of pure functional programming to build fault-tolerant, reactive systems, as well as parallel computing. Another area of his focus is machine learning and natural language processing. 60 | 61 | ## Video by the author 62 | [Spark for Data Analysis in Scala [Video]](https://www.packtpub.com/big-data-and-business-intelligence/spark-data-analysis-scala-video?utm_source=github&utm_medium=repository&utm_campaign=9781787281165 ) 63 | 64 | [](https://www.packtpub.com/application-development/advanced-techniques-data-analysis-scala-video?utm_source=github&utm_medium=repository&utm_campaign=) 65 | 66 | ### Suggestions and Feedback 67 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions. 68 | 69 | ### Download a free PDF 70 | 71 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
72 |

https://packt.link/free-ebook/9781788620796

-------------------------------------------------------------------------------- /Chapter12/jvm/src/main/scala/jvm/HelloWorld.scala: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import scala.language.postfixOps 4 | 5 | import scala.concurrent.{ Future, Await } 6 | import scala.concurrent.ExecutionContext.Implicits.global 7 | import scala.concurrent.duration._ 8 | import scala.util.{ Try, Success, Failure } 9 | 10 | import akka.actor._ 11 | import akka.pattern.{ ask, pipe } 12 | import akka.util.Timeout 13 | import akka.event.Logging 14 | 15 | import cats._, cats.implicits._, cats.data._, cats.effect._ 16 | 17 | object HelloWorld { 18 | case object Ping 19 | 20 | class HelloWorld extends Actor { 21 | val log = Logging(context.system, this) 22 | 23 | def receive = { 24 | case Ping ⇒ log.info("Hello World") 25 | } 26 | } 27 | 28 | def main(args: Array[String]): Unit = { 29 | val system = ActorSystem() 30 | val helloWorld = system.actorOf(Props[HelloWorld], "hello-world") 31 | helloWorld ! Ping 32 | } 33 | } 34 | 35 | object ActorProperties { 36 | class HelloName(name: String) extends Actor { 37 | val log = Logging(context.system, this) 38 | 39 | def receive = { 40 | case "say-hello" ⇒ log.info(s"Hello, $name") 41 | } 42 | } 43 | 44 | object HelloName { 45 | def props(name: String): Props = 46 | Props(classOf[HelloName], name) 47 | } 48 | 49 | def main(args: Array[String]): Unit = { 50 | val system = ActorSystem("hello-custom") 51 | val helloPerson = system.actorOf(HelloName.props("Awesome Person"), "hello-name") 52 | val helloAlien = system.actorOf(HelloName.props("Alien Invaders"), "hello-aliens") 53 | helloPerson ! "say-hello" 54 | helloAlien ! "say-hello" 55 | helloAlien ! "random-msg" 56 | } 57 | } 58 | 59 | object HierarchiesBase { 60 | case class SpawnGreeters(n: Int) 61 | case class SayHello(name: String) 62 | case class Execute(f: () => Unit) 63 | case object JobDone 64 | case class GreetersResolution(result: Try[ActorRef]) 65 | case class GreetersTerminated(result: List[Any]) 66 | case object GreetersCreationAuthorised 67 | case object Die 68 | case object Dead 69 | 70 | 71 | class GreetingsManager extends Actor { 72 | val log = Logging(context.system, this) 73 | 74 | def greeterFromId(id: Any) = s"greeter-$id" 75 | 76 | def resolveGreeters() = 77 | context.actorSelection(greeterFromId("*")).resolveOne(1 second) 78 | .transformWith { 79 | case s@Success(_) => Future.successful(s) 80 | case f@Failure(_) => Future.successful(f) 81 | } 82 | .map(GreetersResolution) pipeTo self 83 | 84 | 85 | def spawningGreeters(requester: ActorRef, numGreeters: Int): Receive = { 86 | case GreetersResolution(Failure(_)) => 87 | self ! GreetersCreationAuthorised 88 | 89 | case GreetersResolution(Success(_)) => 90 | log.warning(s"Greeters already exist. Killing them and creating the new ones.") 91 | context.children 92 | .filter(c => raw"greeter-\d".r.unapplySeq(c.path.name).isDefined) 93 | .toList.traverse(_ ? Die) 94 | .map(GreetersTerminated) pipeTo self 95 | 96 | case GreetersTerminated(report) => 97 | log.info(s"All greeters terminated, report: $report. Creating the new ones now.") 98 | self ! GreetersCreationAuthorised 99 | 100 | case GreetersCreationAuthorised => 101 | (1 to numGreeters).foreach { id => 102 | context.actorOf(Props[Greeter], greeterFromId(id)) } 103 | log.info(s"Created $numGreeters greeters") 104 | requester ! JobDone 105 | context become baseReceive 106 | } 107 | 108 | def sayingHello(requester: ActorRef, msg: Any): Receive = { 109 | case GreetersResolution(Failure(_)) => 110 | log.error("There are no greeters. Please create some first with SpawnGreeters message.") 111 | context become baseReceive 112 | requester ! JobDone 113 | 114 | case GreetersResolution(Success(_)) => 115 | log.info(s"Dispatching message $msg to greeters") 116 | context.actorSelection(greeterFromId("*")) ! msg 117 | context become baseReceive 118 | requester ! JobDone 119 | } 120 | 121 | def baseReceive: Receive = { 122 | case SpawnGreeters(n) => 123 | log.info("Spawning {} greeters", n) 124 | resolveGreeters() 125 | context become spawningGreeters(sender, n) 126 | 127 | case msg@SayHello(_) => 128 | resolveGreeters() 129 | context become sayingHello(sender, msg) 130 | 131 | case "resolve" => 132 | val selection = context.actorSelection(greeterFromId("*")) 133 | selection.resolveOne(1 second).onComplete { res => 134 | log.info(s"Selection: $selection; Res: $res") } 135 | } 136 | 137 | def receive = baseReceive 138 | } 139 | 140 | class Greeter extends Actor { 141 | val log = Logging(context.system, this) 142 | 143 | def receive = { 144 | case SayHello(name) => log.info(s"Greetings to $name") 145 | case Die => 146 | context stop self 147 | sender ! Dead 148 | } 149 | } 150 | 151 | implicit val timeout: Timeout = 3 seconds 152 | val system = ActorSystem("hierarchy-demo") 153 | val gm = system.actorOf(Props[this.GreetingsManager], "greetings-manager") 154 | } 155 | 156 | object HierarchiesResolve extends App { 157 | import HierarchiesBase._ 158 | Await.ready(for { 159 | _ <- Future { gm ! "resolve" } 160 | _ <- gm ? SpawnGreeters(10) 161 | _ <- (1 to 10).toList.traverse(_ => Future { gm ! "resolve" }) 162 | } yield (), 5 seconds) 163 | } 164 | 165 | object HierarchiesDemo extends App { 166 | import HierarchiesBase._ 167 | 168 | def printState(childrenEmpty: Boolean, isHelloMessage: Boolean) = 169 | Future { println(s"\n=== Children: ${if (childrenEmpty) "empty" else "present"}, " + 170 | s"Message: ${if (isHelloMessage) "SayHello" else "SpawnGreeters"}") } 171 | 172 | Await.ready(for { 173 | _ <- printState(true, true) 174 | _ <- gm ? SayHello("me") 175 | 176 | _ <- printState(true, false) 177 | _ <- gm ? SpawnGreeters(3) 178 | 179 | _ <- printState(false, false) 180 | _ <- gm ? SpawnGreeters(3) 181 | 182 | _ <- printState(false, true) 183 | _ <- gm ? SayHello("me") 184 | } yield (), 5 seconds) 185 | } 186 | --------------------------------------------------------------------------------