: D](ancestor: A, descendant: D)
12 |
13 | val grandParentToChild =
14 | new HeirloomTransition(new Grandparent, new Child)
15 |
16 | // Task (4a) -- not an answer, given
17 | // val childToGrandparent =
18 | // new HeirloomTransition(new Child, new Grandparent)
19 |
20 |
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/sbtTutorial/multi/build.sbt:
--------------------------------------------------------------------------------
1 |
2 |
3 | lazy val commonSettings = Seq(
4 | scalaVersion := "2.11.8"
5 | )
6 |
7 | // Set sub-project on SBT start: http://stackoverflow.com/a/22240142/1007926
8 | lazy val root = (project in file(".")).
9 | settings(
10 | onLoad in Global := { Command.process("project backend", _: State) } compose (onLoad in Global).value
11 | ).settings(commonSettings: _*)
12 |
13 |
14 | lazy val backend = (project in file("backend")).
15 | settings(
16 | name := "backend",
17 | version := "1.0"
18 | ).settings(commonSettings:_*)
19 |
20 | lazy val frontend = (project in file("frontend")).
21 | settings(
22 | name := "frontend",
23 | version := "1.0"
24 | ).settings(commonSettings:_*).dependsOn(backend)
25 |
26 |
27 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture5/LookandSaySpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 | import org.scalatest._
4 | import org.scalatest.{FunSuite, Matchers}
5 |
6 | class LookandSaySpec extends FunSuite with Matchers {
7 |
8 | import LookAndSay._
9 |
10 | // val lookAndSaySequence =
11 | // List(1, 1, 1, 3, 1, 2, 2, 1, 1, 3, 3, 1, 1, 2, 1, 3, 2, 1, 1, 3, 2, 1, 2, 2, 2, 1)
12 |
13 | val lookAndSay10 = "13211311123113112211"
14 |
15 | // test(s"10th string in Look and Say sequence is correct: "+lookAndSay10) {
16 | // val result = lookAndSay.
17 | // take(10).
18 | // toListFinite(10)
19 |
20 | // val tenth = result(9)
21 |
22 | // tenth should be (lookAndSay10)
23 |
24 | // }
25 |
26 | }
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sbtTutorial/usingBreezeRefactored/project/Common.scala:
--------------------------------------------------------------------------------
1 | // taken from https://github.com/TrueCar/mleap/blob/master/project/Common.scala
2 |
3 | import sbt._
4 | import Keys._
5 |
6 | object Common {
7 |
8 | // emulate this: https://github.com/TrueCar/mleap/blob/master/project/Common.scala
9 |
10 | // Task 5b
11 | // use Scala 2.11.8
12 | val scalaVer: String = ???
13 |
14 | /*
15 | http://www.scala-sbt.org/0.13/docs/Resolvers.html
16 |
17 | Add resolvers
18 | "bintray/non" at "http://dl.bintray.com/non/maven",
19 | and
20 | "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/"
21 | */
22 |
23 | // Task 5c
24 | lazy val otherResolvers: Seq[Resolver] = ???
25 |
26 | // Task 5a and Task 5g
27 | lazy val commonSettings: Seq[Def.Setting[_]] = ???
28 |
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/tutorialCommon/src/main/scala/com/datascience/education/tutorialCommon/lecture4/EmployeesTypeclass.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorialCommon.lecture4
2 |
3 | import java.util.UUID
4 |
5 | /*
6 | With duplicate definitions of Email, Employee,
7 | and the object that contains the exercises - Employees,
8 | all three of these must be abstractly referenced in these test definitions.
9 | Employee and Employees both necessitate their own typeclass.
10 |
11 | */
12 |
13 | trait EmployeeTypeclass[Email] {
14 | val id: UUID
15 | val email: Email
16 | }
17 |
18 | abstract class EmployeesTypeclass[Employee, Email](
19 | implicit et: Employee => EmployeeTypeclass[Email]) {
20 |
21 | val prianna: Employee
22 | val peter: Employee
23 | val chrisId: UUID
24 |
25 | def employeeEmail(id: UUID): Option[Email]
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/XorList.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import cats.data.Xor
4 | import cats.data.Xor.Left
5 | import cats.data.Xor.Right
6 |
7 | import cats.data.NonEmptyList
8 |
9 | object XorList {
10 | import TraverseXor._
11 | // type XorListException[B] = Xor[NonEmptyList[Exception], B]
12 |
13 | def map2[A, B, C](xorA: XorException[A], xorB: XorException[B])(f: (A, B) => C): XorListException[C] = (xorA, xorB) match {
14 | case (Left(e1), Left(e2)) =>
15 | Left(NonEmptyList(e1, e2)): XorListException[C]
16 | case (Left(e1), Right(b)) =>
17 | Left(NonEmptyList(e1)): XorListException[C]
18 | case (Right(a), Left(e2)) =>
19 | Left(NonEmptyList(e2)): XorListException[C]
20 | case (Right(a), Right(b)) =>
21 | Right(f(a, b))
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/ScanAverage.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 | import com.twitter.algebird.AveragedValue
4 | import com.twitter.algebird.AveragedGroup
5 |
6 | import com.datascience.education.common.lecture5.Stream
7 |
8 |
9 |
10 | object ScanRightExample extends App {
11 |
12 | // 2a
13 | val sum10 = Stream.from(0).take(6).scanRight(0)(_+_)
14 |
15 | sum10.print(10)
16 |
17 |
18 | }
19 |
20 | object Average {
21 |
22 | val incrementingNumbers: Stream[Int] = Stream.from(0)
23 |
24 | // Task 2b
25 |
26 | val average: Stream[AveragedValue] = ???
27 | // incrementingNumbers.take(32).???
28 |
29 | }
30 |
31 | object AverageExample extends App {
32 |
33 | import Average._
34 |
35 | println("average of incrementing numbers")
36 |
37 | average.print(32)
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/XorHelpers.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import cats.data.Xor
4 | import cats.data.Xor.Left
5 | import cats.data.Xor.Right
6 | import cats.data.NonEmptyList
7 |
8 |
9 | object XorHelpers {
10 |
11 | def concatIntoNonEmptyList[A](nel: NonEmptyList[A], l: List[A]): NonEmptyList[A] =
12 | NonEmptyList(nel.head, nel.tail:::l)
13 |
14 | def concatIntoList[A](nel: NonEmptyList[A], l: List[A]): List[A] =
15 | nel.head::nel.tail:::l
16 |
17 | def concat[A](nel1: NonEmptyList[A], nel2: NonEmptyList[A]): NonEmptyList[A] =
18 | NonEmptyList(nel1.head, nel2.head::nel1.tail:::nel2.tail)
19 |
20 |
21 | def toString[A](nel: NonEmptyList[A]): String = {
22 | val h = nel.head
23 | val t = nel.tail
24 | h.toString + " " + t.foldRight("")(_+" "+_)
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | resolvers ++= Seq(
2 | "bintray-sbt-plugins" at "http://dl.bintray.com/sbt/sbt-plugin-releases",
3 | "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/",
4 | "Artima Maven Repository" at "http://repo.artima.com/releases",
5 | "clojars" at "https://clojars.org/repo",
6 | "conjars" at "http://conjars.org/repo",
7 | "plugins" at "http://repo.spring.io/plugins-release",
8 | "sonatype" at "http://oss.sonatype.org/content/groups/public/"
9 | )
10 |
11 | // addSbtPlugin("org.spark-packages" % "sbt-spark-package" % "0.2.4")
12 |
13 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3")
14 |
15 | addSbtPlugin("com.artima.supersafe" % "sbtplugin" % "1.1.0")
16 |
17 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5")
18 |
19 | addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.3")
20 |
21 | addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "0.2.3")
22 |
23 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture1/ImplicitConversions.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture1
2 |
3 |
4 | // Task (5a): decide where the implicit conversion should go; implement it
5 |
6 | object ImplicitConversions {
7 |
8 | type ComplexNumber = (Double, Double)
9 |
10 | trait ComplexVector {
11 | def complexVector: List[ComplexNumber]
12 | override def toString = s"Vector contains $complexVector"
13 | }
14 |
15 |
16 | object ComplexVectors {
17 | def dense(firstValue: ComplexNumber, otherValues: ComplexNumber*): ComplexVector =
18 | new ComplexVector {
19 | val complexVector = firstValue :: otherValues.toList
20 | }
21 | }
22 |
23 |
24 | // val denseInts = ComplexVectors.dense(4, 2, 6, 9)
25 |
26 |
27 | }
28 |
29 | object ImplicitConversionsExample extends App {
30 | import ImplicitConversions._
31 |
32 | // println("Dense Ints")
33 | // println(denseInts)
34 |
35 | }
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture4/FlawedOptionSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import org.scalatest.{FunSuite, Matchers}
4 |
5 | class FlawedOptionSpec extends FunSuite with Matchers {
6 |
7 | test("Methods in FlawedOption should not compile when the type parameter of `FlawedOption` is invariant") {
8 | """
9 | sealed trait FlawedOption[A] {
10 | import FlawedOption._
11 |
12 | def map[B](f: A => B): FlawedOption[B] = this match {
13 | case FlawedSome(a) => FlawedSome(f(a))
14 | case FlawedNone => FlawedNone
15 | }
16 |
17 | def getOrElse(default: => A): A = this match {
18 | case FlawedSome(a) => a
19 | case FlawedNone => default
20 | }
21 | }
22 |
23 | object FlawedOption {
24 | case class FlawedSome[A](get: A) extends FlawedOption[A]
25 | case object FlawedNone extends FlawedOption[Nothing]
26 | }
27 | """ shouldNot compile
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/WebFormVerifier.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | trait WebFormVerifier[+P[_,_] <: Product] {
4 |
5 | private case class VerifiedWebFormImpl(
6 | firstName: String, lastName: String,
7 | phoneNumber: Long, email: String) extends VerifiedWebForm
8 |
9 | protected def constructVerifiedWebForm(
10 | firstName: String, lastName: String,
11 | phoneNumber: Long, email: String): VerifiedWebForm =
12 | VerifiedWebFormImpl(firstName, lastName, phoneNumber, email)
13 |
14 | def isAlpha(string: String): P[String, String]
15 |
16 | def minimumLength(minLength: Int)(string: String): P[String, String]
17 |
18 | def verifyName(name: String): P[String,String]
19 |
20 | def numDigits(num: Long, length: Int): P[String, Long]
21 |
22 | def verifyPhoneNumber(phoneNumber: Long): P[String, Long]
23 |
24 | def verifyEmailAddress(emailAddress: String): P[String, String]
25 |
26 |
27 | def verify(unverifiedWebForm: UnverifiedWebForm): P[String, VerifiedWebForm]
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture4/EmptySetSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import com.datascience.education.tutorialCommon.lecture4.EmptySetTypeclass
4 | import com.datascience.education.tutorialCommon.lecture4.CommonEmptySetSpec
5 |
6 | object EmptySetSpec {
7 |
8 | implicit val emptySetTypeclass = new EmptySetTypeclass {
9 |
10 | def sumList(l: List[Int]): Int = EmptySet.sumList(l)
11 | def prodList(ds: List[Double]): Double = EmptySet.prodList(ds)
12 |
13 | def sumList2(l: List[Int]): Int = EmptySet.sumList2(l)
14 | def prodList2(l: List[Double]): Double = EmptySet.prodList2(l)
15 |
16 | def sum(l: List[Int]): Option[Int] = EmptySet.sum(l)
17 |
18 | def product(l: List[Double]): Option[Double] = EmptySet.product(l)
19 |
20 | def sum2(l: List[Int]): Option[Int] = EmptySet.sum2(l)
21 |
22 | def product2(l: List[Double]): Option[Double] = EmptySet.product2(l)
23 |
24 |
25 | }
26 |
27 | }
28 |
29 | import EmptySetSpec._
30 |
31 | // class EmptySetSpec
32 | // extends CommonEmptySetSpec()(emptySetTypeclass)
33 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture5/QuickSortSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 | import org.scalatest._
4 | import prop._
5 | import org.scalacheck.Gen
6 | import org.scalacheck.Prop
7 | import org.scalacheck.Prop._
8 | import org.scalatest.prop.Checkers.check
9 |
10 |
11 | class QuickSortSpec extends PropSpec
12 | with GeneratorDrivenPropertyChecks with Matchers {
13 | import QuickSort._
14 |
15 | // property("QuickSort on List sorts 16 integers") {
16 | // forAll(
17 | // Gen.listOfN(16, Gen.chooseNum(Integer.MIN_VALUE, Integer.MAX_VALUE))
18 | // ) { unsorted =>
19 | // val correctSorted = unsorted.sorted
20 | // val sorted = quickSort(unsorted).toListFinite(20)
21 | // // http://www.scalatest.org/user_guide/using_matchers#logicalExpressions
22 | // // (
23 | // // correctSorted should be (unsorted)
24 | // // and sorted should be (unsorted)
25 | // // ) or (
26 | // // sorted should be (correctSorted)
27 | // // and sorted should not be (unsorted)
28 | // // )
29 |
30 | // sorted should be (correctSorted)
31 | // }
32 |
33 | // }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/Interpolation.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 | import com.datascience.education.common.lecture5.Stream
4 | import com.datascience.education.common.lecture5.Empty
5 | import com.datascience.education.common.lecture5.Cons
6 | import com.datascience.education.common.lecture5.Stream._
7 |
8 |
9 | object Sine {
10 |
11 | def sinePositive: Stream[Double] =
12 | Stream.cons(0,
13 | Stream.cons(1.0/2,
14 | Stream.cons(math.sqrt(3)/2,
15 | Stream.cons(1.0,
16 | Stream.cons(math.sqrt(3)/2,
17 | Stream.cons(1.0/2, sineNegative)
18 | )
19 | )
20 | )
21 | )
22 | )
23 |
24 | def sineNegative: Stream[Double] =
25 | sinePositive.map { d => -1*d }
26 |
27 |
28 | val sine = sinePositive
29 |
30 |
31 | }
32 |
33 |
34 |
35 |
36 | object Stepper {
37 | import Sine._
38 |
39 | val stepperSine = sine.flatMap { d =>
40 | Stream.cons(d, Stream.cons(d, Stream.empty))
41 | }
42 |
43 |
44 | }
45 |
46 |
47 |
48 | object Interpolation {
49 |
50 | import Sine._
51 |
52 | // Task 1a
53 | val linearInterpolated: Stream[Double] = ???
54 |
55 |
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture1/Hierarchy.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture1
2 |
3 | import scala.language.implicitConversions
4 |
5 | object Hierarchy extends App {
6 |
7 | import org.json4s._
8 | import org.json4s.native.JsonMethods._
9 |
10 |
11 | val json1 = """
12 | {
13 | "firstName": "John",
14 | "lastName": "Smith",
15 | "isAlive": true,
16 | "age": 25,
17 | "address": {
18 | "streetAddress": "21 2nd Street",
19 | "city": "New York",
20 | "state": "NY",
21 | "postalCode": "10021-3100"
22 | },
23 | "phoneNumbers": [
24 | {
25 | "type": "home",
26 | "number": "212 555-1234"
27 | },
28 | {
29 | "type": "office",
30 | "number": "646 555-4567"
31 | }
32 | ],
33 | "children": [],
34 | "spouse": null
35 | }
36 | """
37 |
38 |
39 | val json2 = """
40 | {
41 | "name" : "Bert",
42 | "children" : [
43 | {
44 | "name" : "Alice",
45 | "children" : []
46 | },
47 | {
48 | "name": "Bob",
49 | "children" : [
50 | {
51 | "name" : "Bill",
52 | "children" : []
53 | },
54 | {
55 | "name" : "Zoot",
56 | "children" : []
57 | }
58 | ]
59 | }
60 | ]
61 | }
62 | """
63 |
64 | // Task (1a)
65 |
66 | }
67 |
68 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture4/EmployeesSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import java.util.UUID
4 |
5 | import scala.language.implicitConversions
6 |
7 | import com.datascience.education.tutorialCommon.lecture4.CommonEmployeesSpec
8 |
9 | import Employees._
10 |
11 | import com.datascience.education.tutorialCommon.lecture4.EmployeeTypeclass
12 | import com.datascience.education.tutorialCommon.lecture4.EmployeesTypeclass
13 |
14 | object EmployeesSpec {
15 | implicit def employeeTypeclass(e: Employee): EmployeeTypeclass[Email] =
16 | new EmployeeTypeclass[Email] {
17 | val id = e.id
18 | val email = e.email
19 | }
20 |
21 | implicit def employeesTypeclass:
22 | EmployeesTypeclass[Employee, Email] =
23 | new EmployeesTypeclass[Employee, Email] {
24 | val prianna: Employee = Employees.prianna
25 | val peter: Employee = Employees.peter
26 | val chrisId: UUID = Employees.chrisId
27 | def employeeEmail(id: UUID): Option[Email] =
28 | Employees.employeeEmail(id)
29 | }
30 | }
31 |
32 | import EmployeesSpec._
33 |
34 | // uncomment when ready to test
35 | // class EmployeesSpec
36 | // extends CommonEmployeesSpec()(employeeTypeclass _, employeesTypeclass)
37 |
38 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/ResponseList.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import scala.util.Random
4 |
5 | import cats.data.Xor
6 | import cats.data.Xor.Left
7 | import cats.data.Xor.Right
8 |
9 | object ResponseList {
10 |
11 | import RequestResponse._
12 | import TraverseXor._
13 |
14 | val rand = new Random
15 |
16 | val listRequests = (1 to 10).map((i: Int) => Request(i.toString)).toList
17 | val listCorrupt = Request("foo") :: listRequests ::: List(Request("bar"), Request("5"))
18 |
19 | // Task 4a
20 | def parsePayload(payload: Payload): Xor[NumberFormatException, Int] =
21 | ???
22 |
23 | // Task 4b
24 | def pipeline(request: Request): Xor[Exception, Int] =
25 | ???
26 |
27 | // Task 4c
28 | def sum(lr: List[Request]): XorException[Int] =
29 | ???
30 | }
31 |
32 | object ResponseListExample extends App {
33 | import ResponseList._
34 |
35 | println("List of valid Requests:")
36 | println(listRequests)
37 |
38 |
39 | val sumValid = sum(listRequests)
40 |
41 | println(sumValid)
42 |
43 | println("----------------------")
44 | println("List of possibly corrupt Payloads:")
45 | println(listCorrupt)
46 |
47 | val sumCorrupt = sum(listCorrupt)
48 |
49 | println(sumCorrupt)
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/tutorialCommon/src/test/scala/com/datascience/education/tutorialCommon/lecture4/CommonEmptySetSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorialCommon.lecture4
2 |
3 | import org.scalatest.{FunSuite, Matchers}
4 |
5 | class CommonEmptySetSpec[EmptySet](implicit EmptySet: EmptySetTypeclass) extends FunSuite with Matchers {
6 |
7 | import EmptySet._
8 | test("Sum of 1 through 10 inclusive should be 55; method sum") {
9 | sum((1 to 10).toList) should be (Some(55))
10 | }
11 |
12 | val dec = (1 to 10).map(_.toDouble).toList
13 | val prod = 3628800.0
14 |
15 | test(s"Product of $dec should be $prod ; prodList") {
16 | prodList(dec) should be (prod)
17 | }
18 | test(s"Product of $dec should be $prod ; prodList2") {
19 | prodList2(dec) should be (prod)
20 | }
21 |
22 | test(s"Sum of empty List should be None ; sum2") {
23 | sum2(List[Int]()) should be (None)
24 | }
25 |
26 | val oneTen = (1 to 10).toList
27 | val sm = oneTen.sum
28 |
29 | test(s"Sum of $oneTen should be Some($sm) ; sum2") {
30 | sum2(oneTen) should be (Some(sm))
31 | }
32 |
33 |
34 | test(s"Product of empty List should be None ; product2") {
35 | product2(List[Double]()) should be (None)
36 | }
37 |
38 | test(s"Product of $dec should be Some($prod) ; product2") {
39 | product2(dec) should be (Some(prod))
40 | }
41 |
42 |
43 |
44 |
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/sbtTutorial/usingBreezeRefactored/project/Dependencies.scala:
--------------------------------------------------------------------------------
1 | // taken from https://github.com/TrueCar/mleap/blob/master/project/Dependencies.scala
2 |
3 | import sbt._
4 |
5 | object Dependencies {
6 |
7 | import Common.scalaVer
8 |
9 | // emulate this: https://github.com/TrueCar/mleap/blob/master/project/Dependencies.scala
10 |
11 | // Task 5f
12 |
13 | // http://www.scala-sbt.org/1.0/docs/Compiler-Plugins.html
14 |
15 | // https://github.com/non/kind-projector
16 | // http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22kind-projector_2.11%22
17 | val kindProjector = compilerPlugin("org.spire-math" % "kind-projector" % "0.9.0" cross CrossVersion.binary)
18 |
19 | // https://github.com/milessabin/si2712fix-plugin
20 | // http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22si2712fix-plugin_2.11.8%22
21 | val si2712 = compilerPlugin("com.milessabin" % "si2712fix-plugin" % "1.2.0" cross CrossVersion.full)
22 |
23 | // formatting of dependencies
24 | // http://www.scala-sbt.org/0.13/docs/Library-Dependencies.html#The++key
25 |
26 | // Task 5e
27 | val breezeVersion: String = ???
28 |
29 | // Task 5d
30 | lazy val commonDependencies: Seq[ModuleID] = ???
31 |
32 | // Separate "common dependencies" from "plotting dependencies"
33 | // Do not write the same Maven artifact twice
34 |
35 | // Task 5d
36 | lazy val plottingDependencies: Seq[ModuleID] = ???
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture3/DatabaseSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture3
2 |
3 | import org.scalatest._
4 | import prop._
5 |
6 | import org.scalacheck.Gen
7 | import org.scalacheck.Prop
8 | import org.scalacheck.Prop.forAllNoShrink
9 |
10 | import org.scalatest.prop.Checkers.check
11 |
12 | import cats.data.Reader
13 | import cats.syntax.applicative._
14 |
15 | class DatabaseSpec extends PropSpec with
16 | GeneratorDrivenPropertyChecks with Matchers {
17 |
18 |
19 | import DatabaseQueriesAndUpdates._
20 |
21 | // Task 3a
22 | // property("User does not exist in database") {
23 | //
24 | // val testDB = TestDatabase()
25 | //
26 | // forAll(Gen.alphaStr) {
27 | // case (username: String) =>
28 | // userExists(username).run(testDB) should be (false)
29 | // }
30 | //
31 | // }
32 |
33 |
34 | // val nonEmptyString = Gen.nonEmptyContainerOf(Gen.alphaStr)
35 |
36 | // val nonEmptyString = Gen.chooseNum(8, 32).flatMap { length =>
37 | // Gen.listOfN(length, Gen.alphaChar).map(_.mkString)
38 | // }
39 |
40 |
41 | // Task 3b
42 | // property("create user") {
43 | // ???
44 | // }
45 |
46 |
47 | // Task 3c
48 | // property("User exists in database") {
49 | //
50 | // ???
51 | // }
52 |
53 | // Task 3d
54 | // property("Password works") {
55 | // ???
56 | //
57 | // }
58 |
59 |
60 | // Task 3e
61 | // property("Bad password fails") {
62 | //
63 | // ???
64 | // }
65 |
66 | }
67 |
68 |
69 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4/Employees.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import scala.collection.Map
4 |
5 | import java.util.UUID
6 |
7 | object Employees {
8 |
9 |
10 | type Email = String
11 |
12 | case class Employee(id: UUID, firstName: String, email: Email, ssnLast4: Short)
13 |
14 | val prianna = Employee(UUID.randomUUID(), "Prianna", "prianna@datascience.com", 8765)
15 | val peter = Employee(UUID.randomUUID(), "Peter", "peter@datascience.com", 9876)
16 |
17 | val chrisId = UUID.randomUUID()
18 |
19 | type Employees = Map[UUID, Employee]
20 | val employees: Employees = Map[UUID, Employee](prianna.id -> prianna, peter.id -> peter)
21 |
22 | /*
23 | Alternative problem description
24 |
25 | Implement the function "employeeEmail(id: UUID): Option[Email]",
26 | which returns an e-mail address if an employee exists who has the provided ID.
27 |
28 | Use the "get" method on Map.
29 | http://www.scala-lang.org/api/current/index.html#scala.collection.Map@get(key:A):Option[B]
30 |
31 | */
32 |
33 | // Task (3a)
34 | def employeeEmail(id: UUID): Option[Email] =
35 | ???
36 |
37 | }
38 |
39 | object EmployeesExample extends App {
40 | import Employees._
41 |
42 | val priannaEmail = employeeEmail(prianna.id)
43 | println(s"Prianna's email: $priannaEmail")
44 | val peterEmail = employeeEmail(peter.id)
45 | println(s"Peter's email: $peterEmail")
46 | val chrisEmail = employeeEmail(chrisId)
47 | println(s"Chris' email: $chrisEmail")
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/LookandSay.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 |
4 | import scala.math.BigInt
5 |
6 | import com.datascience.education.common.lecture5.Stream
7 | import com.datascience.education.common.lecture5.Empty
8 | import com.datascience.education.common.lecture5.Cons
9 | import com.datascience.education.common.lecture5.Stream._
10 |
11 | /*
12 |
13 | https://en.wikipedia.org/wiki/Look-and-say_sequence
14 |
15 | To generate a member of the sequence from the previous member, read off the digits of the previous member, counting the number of digits in groups of the same digit. For example:
16 |
17 | 1 is read off as "one 1" or 11.
18 | 11 is read off as "two 1s" or 21.
19 | 21 is read off as "one 2, then one 1" or 1211.
20 | 1211 is read off as "one 1, then one 2, then two 1s" or 111221.
21 | 111221 is read off as "three 1s, then two 2s, then one 1" or 312211.
22 |
23 |
24 | */
25 |
26 | object LookAndSay {
27 |
28 | /*
29 | https://www.rosettacode.org/wiki/Look-and-say_sequence#Scala
30 |
31 | Embed the function `next` into a Stream with `unfold`
32 |
33 | So if a single call to `next` provides the next integer in the Look And Say sequence,
34 | `unfold` will produce the next value in the Stream with `next`
35 |
36 | */
37 |
38 | // Task 3a
39 |
40 | val lookAndSay: Stream[String] = ???
41 | // Stream.unfold???
42 |
43 | }
44 |
45 | object LookAndSayExample extends App {
46 | import LookAndSay._
47 |
48 |
49 | lookAndSay.print(10)
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture1/TypeClassProblem.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture1
2 |
3 | import scala.language.higherKinds
4 |
5 | object ModelProblem {
6 |
7 | // Task (6a)
8 | trait Plottable[Domain, Range, T[Domain, Range]] {
9 | // The `= ???` is necessary to prevent compiler error; but this should be abstract!
10 | // Remove `= ???` upon implementation of the signature of `points`
11 | def points/*???*/ = ???
12 |
13 |
14 | def plot(name: String, t: T[Domain, Range], input: List[Domain]): Unit = {
15 | //val y = points(t, input)
16 | val y = ???
17 | println(s"$name: $y")
18 | }
19 |
20 | }
21 |
22 | class Model[Domain, Range](val pdf: Domain => Range)
23 |
24 | // Task (6b)
25 | implicit object PlotDoubleDoubleModel extends Plottable[Double, Double, Model] {
26 |
27 | ???
28 | }
29 |
30 | // Task (6c)
31 | implicit object PlotDoubleDoubleFunction extends Plottable[Double, Double, Function1] {
32 |
33 | ???
34 | }
35 |
36 | // Task (6d)
37 | def plotter[D,R,T[D,R]](t: T[D,R], ld: List[D], name: String)
38 | (implicit plottable: Plottable[D,R,T]): Unit = ???
39 |
40 |
41 | def gaussianPDF(u: Double, t: Double)(d: Double): Double =
42 | (1/(math.sqrt(2*t*t*math.Pi)))*math.pow(math.E, -1*math.pow(d - u, 2)/(2*t*t))
43 |
44 | val gaussianModel = new Model(gaussianPDF(1.0,2.0))
45 |
46 | }
47 |
48 | object PlotExample extends App {
49 |
50 | import ModelProblem._
51 |
52 |
53 | val x = (-16 to 16).toList.map(_.toDouble).map(_/10.0)
54 | plotter(gaussianModel, x, "gaussianpdf.png")
55 |
56 |
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/project/Common.scala:
--------------------------------------------------------------------------------
1 | // taken from https://github.com/TrueCar/mleap/blob/master/project/Common.scala
2 |
3 | import sbt._
4 | import Keys._
5 | import sbtunidoc.Plugin.UnidocKeys._
6 | import sbtunidoc.Plugin.ScalaUnidoc
7 |
8 |
9 | object Common {
10 |
11 | val scalaVer = "2.11.8"
12 |
13 |
14 | lazy val otherResolvers = Seq(
15 | "bintray/non" at "http://dl.bintray.com/non/maven",
16 | "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/",
17 | "sonatype" at "http://oss.sonatype.org/content/groups/public/",
18 | "twitter-repo" at "https://maven.twttr.com",
19 | "Clojars Repository" at "http://clojars.org/repo",
20 | "conjars" at "http://conjars.org/repo",
21 | "Artima Maven Repository" at "http://repo.artima.com/releases",
22 | //"Spark Packages Repo Bintray" at "http://dl.bintray.com/spark-packages/maven",
23 | Opts.resolver.sonatypeSnapshots
24 | )
25 |
26 |
27 | lazy val settings: Seq[Def.Setting[_]] = Seq(
28 | version := "0.1",
29 | scalaVersion := scalaVer,
30 | organization := "com.datascience.education",
31 | scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"),
32 | resolvers ++= otherResolvers,
33 | resolvers += Resolver.url(
34 | "bintray-sbt-plugins",
35 | url("http://dl.bintray.com/sbt/sbt-plugins"))(
36 | Resolver.ivyStylePatterns),
37 | libraryDependencies ++= Dependencies.dependencies
38 | )
39 |
40 | lazy val additionalUnidocSettings: Seq[Def.Setting[_]] = Seq(
41 | target in unidoc in ScalaUnidoc := baseDirectory.value / "doc" / "scaladoc",
42 | autoAPIMappings := true
43 | )
44 |
45 |
46 |
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture3/AsynchronousFactorials.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture3
2 |
3 | import scala.concurrent._
4 |
5 | import scala.concurrent.ExecutionContext.Implicits.global
6 |
7 | object AsynchronousFactorial {
8 |
9 | def factorial(n: Int): Int = {
10 | val fact = if(n == 0) 1 else n*factorial(n-1)
11 |
12 | Thread.sleep(500)
13 | println(s"factorial($n) = $fact")
14 |
15 | fact
16 | }
17 |
18 | def factorialAsync(n: Int): Future[Int] = Future(factorial(n))
19 |
20 | def printFactorial(n: Int): Future[Int] = {
21 | val fut: Future[Int] = factorialAsync(n)
22 |
23 | fut.onSuccess {
24 | case f => println(s"factorial $n is $f")
25 | }
26 |
27 | fut
28 | }
29 |
30 | }
31 |
32 | object AsynchronousFactorialsExample extends App {
33 |
34 | import AsynchronousFactorial._
35 |
36 | val fut10 = printFactorial(10)
37 | val fut20 = printFactorial(20)
38 |
39 | (1 to 30).foreach(i => {Thread.sleep(500); println(s"unrelated: $i")})
40 |
41 | }
42 |
43 | import cats.syntax.applicative._
44 | import cats.syntax.writer._
45 | import cats.data.Writer
46 |
47 | object FactorialWriter {
48 |
49 | // Task (1a)
50 |
51 | // type Logged[A] = ???
52 |
53 | // Task (1b)
54 | // def factorial(n: Int): ??? = ???
55 |
56 |
57 | // Task 1c
58 | // def factorialAsync(n: Int): ??? = ???
59 |
60 |
61 | }
62 |
63 | object FactorialWriterExample extends App {
64 | import FactorialWriter._
65 |
66 | // Task 1b
67 |
68 | }
69 |
70 | object FactorialWriterAsyncExample extends App {
71 | import FactorialWriter._
72 |
73 |
74 | // Task 1d
75 |
76 |
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/slides/theme/css/print.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | @page {
7 | size: landscape;
8 | }
9 |
10 | body {
11 | font: 100% "Lucida Grande", "Trebuchet MS", Verdana, sans-serif;
12 | padding: 0;
13 | margin: 0;
14 | }
15 |
16 | div.slide {
17 | min-width: 800px;
18 | min-height: 600px;
19 | padding: 1em;
20 | overflow: hidden;
21 | page-break-after: always;
22 | border: 1px solid black;
23 | border-radius: 20px;
24 | }
25 |
26 | div.slide div.inner {
27 | width: 800px;
28 | height: 600px;
29 | margin: auto;
30 | display: table-cell;
31 | }
32 |
33 | h1 {
34 | font-size: 2.4em;
35 | }
36 |
37 | h2 {
38 | font-size: 1.4em;
39 | }
40 |
41 | h3 {
42 | margin: 1em 0;
43 | }
44 |
45 | ul {
46 | margin: 0;
47 | padding: 0;
48 | }
49 |
50 | p, li, pre {
51 | margin: 1em 0;
52 | }
53 |
54 | li {
55 | margin-left: 2em;
56 | }
57 |
58 | a {
59 | color: #000000;
60 | }
61 |
62 | pre, code {
63 | max-width: 800px;
64 | background: #eee;
65 | font-family: Monaco, monospace;
66 | font-size: 90%;
67 | }
68 |
69 | pre {
70 | padding: .2em .5em;
71 | overflow: hidden;
72 | border-radius: .8em;
73 | }
74 |
75 | code {
76 | padding: 0 .2em;
77 | }
78 |
79 | .slide header:only-child h1 {
80 | line-height: 180%;
81 | text-align: center;
82 | display: table-cell;
83 | vertical-align: middle;
84 | height: 600px;
85 | width: 800px;
86 | font-size: 48px;
87 | margin-top:100px;
88 | margin-bottom:100px;
89 | }
90 |
91 | #toc, #help, .slide aside, .slide footer, .slide .notes,
92 | .presenter_notes, #current_presenter_notes, #presenter_note {
93 | display: none;
94 | }
95 |
--------------------------------------------------------------------------------
/params.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Scala",
3 | "tagline": "Lecture slides, labs, and tutorials from our 8-week Scala course.",
4 | "body": "#Lecture Slides\r\n\r\n\r\n\r\nLectures 4-13a are numbered according to their corresponding chapter in _Functional Programming in Scala_.",
5 | "note": "Don't delete this file! It's used internally to help with page regeneration."
6 | }
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/InterpolationExamples.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 |
4 | object SineExample extends App {
5 | import Sine._
6 | import breeze.linalg._
7 | import breeze.plot._
8 |
9 | sine.print(32)
10 |
11 | val f = Figure()
12 | val p = f.subplot(0)
13 | val x = linspace(0.0,2.5*math.Pi, 16)
14 |
15 | val out: List[Double] = sine.toListFinite(16)
16 |
17 | p+= plot(x, out)
18 | p+= plot(x, out, '.')
19 |
20 | p.xlabel = "theta radians"
21 | p.ylabel = "sin(theta)"
22 |
23 | f.saveas("sine_wave.png")
24 |
25 | println("done plotting")
26 |
27 |
28 |
29 | }
30 |
31 |
32 | object StepperExample extends App {
33 | import Stepper._
34 |
35 | import breeze.linalg._
36 | import breeze.plot._
37 |
38 |
39 | val stepperOut = stepperSine.toListFinite(32)
40 |
41 | val f = Figure()
42 | val p2 = f.subplot(0)
43 | val x2 = linspace(0.0,2.5*math.Pi, 32)
44 |
45 | p2 += plot(x2, stepperOut)
46 | p2 += plot(x2, stepperOut, '.')
47 |
48 | p2.xlabel = "theta radians"
49 | p2.ylabel = "sin(theta)"
50 |
51 | f.saveas("sine_wave_stepper.png")
52 |
53 | println("done plotting")
54 |
55 | }
56 |
57 | object InterpolationExample extends App {
58 |
59 | import Interpolation._
60 |
61 | import breeze.linalg._
62 | import breeze.plot._
63 |
64 | val linearInterpolatedOut = linearInterpolated.toListFinite(32)
65 |
66 | println(linearInterpolatedOut)
67 |
68 | val f = Figure()
69 | val p3 = f.subplot(0)
70 | val x3 = linspace(0.0,2.5*math.Pi, 32)
71 |
72 | p3 += plot(x3, linearInterpolatedOut)
73 | p3 += plot(x3, linearInterpolatedOut, '.')
74 |
75 | p3.xlabel = "theta radians"
76 | p3.ylabel = "sin(theta)"
77 |
78 | f.saveas("sine_wave_linear_interpolation.png")
79 |
80 | println("done plotting")
81 |
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Functional Scala for Data Engineers
2 |
3 | A fast-paced introduction to Scala's FP ecosystem for data engineers.
4 |
5 | These slides loosely follow Chiusano and Bjarnason's [Functional Programming in Scala](https://www.manning.com/books/functional-programming-in-scala) with an emphasis on introducing Cats and various other FP libraries. Please read chapters 1-3 before beginning.
6 |
7 | Welsh and Gurnell's [Advanced Scala with Cats](http://underscore.io/books/advanced-scala/) is an optional supplementary text, and their [Essential Scala](http://underscore.io/books/essential-scala/) is a minimal prerequisite for the course. The first two courses in Coursera's [Functional Programming in Scala Specialization](https://www.coursera.org/specializations/scala) are also highly recommended.
8 |
9 | Slides are viewable [here](http://DS12.github.io/scala-class).
10 |
11 | [](https://gitter.im/ds12/lobby)
12 |
13 | A link to ScalaDoc will be available in the slide table of contents, above.
14 |
15 | Functional Scala for Data Engineers is licensed under a Creative Commons CC0 1.0 Universal License .
16 |
17 |
18 | `DS12/scala-class` build status
19 |
20 | [](https://travis-ci.org/DS12/scala-class)
21 |
22 | `DS12/scala-class-private` build status
23 |
24 | [](https://travis-ci.com/DS12/scala-class-private)
25 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture4/SafeDivisionSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import org.scalatest.{FunSuite, Matchers}
4 |
5 | import SafeDivision._
6 |
7 | class SafeDivisionSpec extends FunSuite with Matchers {
8 |
9 | // // https://docs.oracle.com/javase/7/docs/api/java/lang/Double.html
10 | // test("safeDiv catches Double.POSITIVE_INFINITY") {
11 | // safeDiv(5, 0) should be (None)
12 | // }
13 | // test("safeDiv catches Double.NEGATIVE_INFINITY") {
14 | // safeDiv(-5, 0) should be (None)
15 | // }
16 | // test("safeDiv catches Double.NaN") {
17 | // safeDiv(0, 0) should be (None)
18 | // }
19 | //
20 | // val l1 = (6 to 11).toList
21 | // val l2 = (2 to 7).toList
22 | // val fracsSuccessful: List[(Int, Int)] = l1.zip(l2)
23 | //
24 | // // http://www.scalatest.org/user_guide/using_matchers#checkingAnObjectsClass
25 | // test(s"All of these fractions {$fracsSuccessful} are real numbers, so the traversal should succeed") {
26 | // val out: Option[List[Double]] = traverseFractions(fracsSuccessful)
27 | // out shouldBe a [Some[_]]
28 | // }
29 | //
30 | // val l3 = (6 to 11).toList
31 | // val l4 = (-3 to 2).toList
32 | // val fracsFailing: List[(Int, Int)] = l3.zip(l4)
33 | //
34 | // test(s"One of these fractions {$fracsFailing} is not a real number, so the traversal should fail") {
35 | // val out: Option[List[Double]] = traverseFractions(fracsFailing)
36 | // out shouldBe None
37 | // }
38 | //
39 | // test(s"All of these fractions {$fracsSuccessful} are real numbers, so the traversal and square root should succeed") {
40 | // val out: Option[List[Double]] = traverseSqrtFractions(fracsSuccessful)
41 | // out shouldBe a [Some[_]]
42 | // }
43 | //
44 | // test(s"One of these fractions {$fracsFailing} is not a real number, so the traversal and square root should fail") {
45 | // val out: Option[List[Double]] = traverseSqrtFractions(fracsFailing)
46 | // out shouldBe None
47 | // }
48 |
49 | }
--------------------------------------------------------------------------------
/tutorialCommon/src/test/scala/com/datascience/education/tutorialCommon/lecture4/CommonEmployeesSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorialCommon.lecture4
2 |
3 | import org.scalatest.{FunSuite, Matchers}
4 |
5 | /*
6 | The typeclass pattern may be useful to unify test implementations
7 | between exercise and answer SBT sub-projects.
8 |
9 | The test is defined on a typeclass, which both
10 | exercise and answer code implement -- see
11 | com.datascience.education.tutorialAnswer.lecture4.EmptySetSpec and
12 | com.datascience.education.tutorial.lecture4.EmptySetSpec
13 | in the test folder for a simpler example than `Employees`.
14 |
15 | `EmptySet` is too simple to demonstrate the complication of this attempt
16 | at test unification.
17 | `Employees` features a couple of custom types:
18 | the `Email` type, and the `Employee` case class.
19 | These are duplicately defined in the exercise and answer code,
20 | to make them immediately visible to the student. We should not
21 | hide these simple types away in this `tutorialCommon` sub-project.
22 | While testing boilerplate may expand, I attempt to keep the exercises
23 | in `main` as compact as possible.
24 | `test` code is generally my responsibility and a "given" for the student.
25 |
26 | Both this attempt and the monomorphism attempt at test unification
27 | (see com.datascience.education.tutorialCommon.lecture4.FPOption)
28 | will be shelved for now. Test implementations will simply be duplicated
29 | between the `tutorial` and `tutorialAnswer` sub-projects.
30 |
31 | */
32 |
33 | class CommonEmployeesSpec[Employee, Employees, Email](
34 | implicit
35 | et: Employee => EmployeeTypeclass[Email], Employees: EmployeesTypeclass[Employee, Email]
36 | ) extends FunSuite with Matchers {
37 |
38 | import Employees._
39 |
40 | // http://www.scalatest.org/user_guide/using_matchers
41 | test("Peter's email should be wrapped in a Some") {
42 | employeeEmail(peter.id) should be (Some(peter.email))
43 | }
44 |
45 | test("Chris's email should be a None") {
46 | employeeEmail(chrisId) should be (None)
47 | }
48 |
49 | }
50 |
51 |
52 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/QuickSort.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture5
2 |
3 | import com.datascience.education.common.lecture5.Stream
4 | import com.datascience.education.common.lecture5.Empty
5 | import com.datascience.education.common.lecture5.Cons
6 | import com.datascience.education.common.lecture5.Stream._
7 |
8 |
9 | object QuickSort {
10 |
11 | // Task 4c
12 | // Answer 4c
13 | def quickSort(si: Stream[Int]): Stream[Int] = {
14 | println("call")
15 | ???
16 | }
17 |
18 | def quickSort(li: List[Int]): Stream[Int] =
19 | quickSort(listToStream(li))
20 |
21 | }
22 |
23 |
24 |
25 | object QuickSortExample extends App {
26 |
27 | import QuickSort._
28 |
29 | import scala.util.Random
30 | val rand = new Random()
31 |
32 | val unsorted = (for (_ <- 1 to 16) yield rand.nextInt(64)).toList
33 |
34 | println("unsorted")
35 |
36 | println(unsorted)
37 |
38 | println("---------")
39 |
40 |
41 | val sortedLazy = quickSort(unsorted)
42 | println("sort 4 digits")
43 | sortedLazy.print(4)
44 |
45 | println()
46 |
47 | println("-----------------------")
48 |
49 |
50 | val sortedLazy2 = quickSort(unsorted)
51 | println("sort 8 digits")
52 | sortedLazy2.print(8)
53 |
54 | println()
55 |
56 | println("-----------------------")
57 |
58 | val sortedLazy3 = quickSort(unsorted)
59 | println("least element / head")
60 | println(sortedLazy3.headOption)
61 |
62 |
63 | println("---------------------------")
64 |
65 | println("re-use of `sortedLazy`; demonstration of memoization")
66 |
67 | println("sort 4 digits")
68 | sortedLazy.print(4)
69 |
70 | println("sort 6 digits")
71 | sortedLazy.print(6)
72 |
73 |
74 |
75 | }
76 |
77 | object QuickSortUnevaluated extends App {
78 | import QuickSort._
79 |
80 | import scala.util.Random
81 | val rand = new Random()
82 |
83 | val unsorted = (for (_ <- 1 to 16) yield rand.nextInt(64)).toList
84 |
85 | println("---------")
86 |
87 |
88 | val sortedLazy = quickSort(unsorted)
89 | println("sort 4 digits")
90 | sortedLazy.print(4)
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4/EmptySet.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 |
4 | object EmptySet {
5 | def sumList(l: List[Int]): Int = l match {
6 | case Nil => 0
7 | case x :: xs => x + sumList(xs)
8 | }
9 | def prodList(ds: List[Double]): Double = ds match {
10 | case Nil => 1.0
11 | case x :: xs => x * prodList(xs)
12 | }
13 |
14 | def sumList2(l: List[Int]) = l.foldRight(0)(_ + _)
15 | def prodList2(l: List[Double]) = l.foldRight(1.0)(_ * _)
16 |
17 |
18 |
19 | // Task (1a)
20 | def sum(l: List[Int]): Option[Int] = ???
21 |
22 | // Task (1b)
23 | def product(l: List[Double]): Option[Double] = ???
24 |
25 |
26 | // Task (1c)
27 | def sum2(l: List[Int]): Option[Int] = ???
28 |
29 | // Task (1d)
30 |
31 | def product2(l: List[Double]): Option[Double] = ???
32 |
33 |
34 | }
35 |
36 |
37 | object EmptySetExamples extends App {
38 |
39 | import EmptySet._
40 |
41 | val nums = (1 to 10).toList
42 | val dec = nums.map(_.toDouble/10.0)
43 |
44 | val s10 = sum(nums)
45 |
46 | println(s"sum of $nums")
47 | println(s10)
48 |
49 | val emptyInt = List[Int]()
50 |
51 | val sEmpty = sum(emptyInt)
52 |
53 | println(s"sum of $emptyInt")
54 | println(sEmpty)
55 |
56 |
57 | println("--------------------")
58 |
59 |
60 | val p10 = product(dec)
61 |
62 | println(s"product of $dec")
63 | println(p10)
64 |
65 | val emptyDouble = List[Double]()
66 |
67 | val dEmpty = product(emptyDouble)
68 |
69 | println(s"product of $emptyDouble")
70 | println(dEmpty)
71 |
72 |
73 | println("--------------------")
74 |
75 | val sFolded10 = sum2(nums)
76 | println(s"sum of $nums")
77 | println(sFolded10)
78 |
79 | val sFoldedEmpty = sum2(emptyInt)
80 | println(s"sum of $emptyInt")
81 | println(sFoldedEmpty)
82 |
83 | println("--------------------")
84 |
85 |
86 | val pFolded10 = product2(dec)
87 |
88 | println(s"product of $dec")
89 | println(pFolded10)
90 |
91 | val dFoldedEmpty = product2(emptyDouble)
92 |
93 | println(s"product of $emptyDouble")
94 | println(dFoldedEmpty)
95 |
96 |
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/tutorial/src/test/scala/com/datascience/education/tutorial/lecture4/FPOptionSpec.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import org.scalatest.{FunSuite, Matchers}
4 |
5 | class FPOptionSpec extends FunSuite with Matchers {
6 | import FPOption._
7 |
8 | // val optionHello: FPOption[String] = FPSome("hello")
9 | // val option65: FPOption[Int] = FPSome(65)
10 | // val noInt: FPOption[Int] = FPNone
11 | // val option64: FPOption[Int] = unit(64) // alternative construction
12 |
13 | // def capitalLetter(i: Int): FPOption[Char] =
14 | // if (i >= 65 && i <= 90) FPSome(i.toChar) // use unit here
15 | // else FPNone
16 |
17 |
18 | // test(s"The inner type of $option65 .getOrElse($optionHello) is raised to common ancestor `Any`") {
19 | // "option65.orElse(optionHello): FPOption[Any]" should compile
20 | // }
21 |
22 | // // http://www.scalatest.org/user_guide/using_matchers#checkingThatCodeDoesNotCompile
23 | // test(s"The inner type of $option65 .getOrElse($optionHello) should not be Int") {
24 | // "val foo: FPOption[Int] = option65.orElse(optionHello)" shouldNot typeCheck
25 | // }
26 |
27 | // test(s"The inner type of $option65 .getOrElse($optionHello) should not be String") {
28 | // "val foo: FPOption[String] = option65.orElse(optionHello)" shouldNot typeCheck
29 | // }
30 |
31 | // test(s"The ASCII capital character for code 65 is FPSome('A')") {
32 | // capitalLetter(65) shouldBe (FPSome('A'))
33 | // }
34 |
35 | // test(s"The ASCII capital character for code 64 is FPNone") {
36 | // capitalLetter(64) shouldBe (FPNone)
37 | // }
38 |
39 | // test(s"option65.flatMap(capitalLetter) is FPSome('A')") {
40 | // option65.flatMap(capitalLetter) shouldBe (FPSome('A'))
41 | // }
42 |
43 | // test(s"noInt.flatMap(capitalLetter) is FPNone") {
44 | // noInt.flatMap(capitalLetter) shouldBe (FPNone)
45 | // }
46 |
47 | // test(s"The sum of FPSome(64) and FPSome(65) is FPSome(129)") {
48 | // option64.map2(option65)(_+_) shouldBe (FPSome(129))
49 | // }
50 |
51 | // test(s"The sum of FPSome(64) and FPSome(65) should not be FPNone") {
52 | // option64.map2(option65)(_+_) should not be (FPNone)
53 | // }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4/SafeDivision.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 |
4 | import scala.Option
5 | import scala.Some
6 | import scala.None
7 |
8 | import cats.Applicative
9 | import cats.Traverse
10 | import cats.std.list._
11 |
12 |
13 | object SafeDivision {
14 |
15 | // Task (2a)
16 | def safeDiv(x: Int, y: Int): Option[Double] =
17 | ???
18 |
19 | def divTuple(tup: (Int, Int)): Option[Double] =
20 | safeDiv(tup._1, tup._2)
21 |
22 |
23 | import TraverseOption._
24 |
25 | // Task (2b)
26 | def traverseFractions(ll: List[(Int, Int)]): Option[List[Double]] =
27 | ???
28 |
29 |
30 | // Task (2c)
31 | def traverseSqrtFractions(ll: List[(Int, Int)]): Option[List[Double]] =
32 | ???
33 |
34 | }
35 |
36 |
37 | object SafeDivisionExamples extends App {
38 |
39 | import SafeDivision._
40 |
41 | println("Divide 7 by 2")
42 | println(divTuple((7,2)))
43 |
44 | println("Divide 7 by 0")
45 | println(divTuple((7,0)))
46 |
47 | }
48 |
49 | object SafeDivisionTraversalExamples extends App {
50 | import SafeDivision._
51 |
52 | val a = (6 to 11).toList
53 | val b = (-3 to 2).toList
54 | val fracsFailing: List[Tuple2[Int, Int]] = a.zip(b)
55 |
56 | val optionList1: Option[List[Double]] =
57 | traverseFractions(fracsFailing)
58 |
59 | println("Option[List[Double]] in one step, using `traverse`: ")
60 | println("should fail")
61 | println(optionList1)
62 |
63 |
64 | println("-----------------")
65 | val c = (6 to 11).toList
66 | val d = (2 to 7).toList
67 | val fracsSuccessful: List[Tuple2[Int, Int]] = c.zip(d)
68 |
69 | println("These fractions do not include an undefined number")
70 | val optionList2: Option[List[Double]] =
71 | traverseFractions(fracsSuccessful)
72 |
73 | println("Option[List[Double]] in one step, using `traverse`: ")
74 | println(optionList2)
75 |
76 | println("-----------------")
77 |
78 | val optionSqrt1: Option[List[Double]] = traverseSqrtFractions(fracsFailing)
79 | println("Square root of fractions using `traverse`: ")
80 | println("Should fail")
81 | println(optionSqrt1)
82 |
83 | val optionSqrt2: Option[List[Double]] = traverseSqrtFractions(fracsSuccessful)
84 | println("Square root of fractions using `traverse`: ")
85 | println("These fractions do not include an undefined number and should succeed")
86 | println(optionSqrt2)
87 |
88 | }
89 |
90 |
91 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture3/Database.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture3
2 |
3 |
4 | import cats.data.Reader
5 | import cats.syntax.applicative._
6 |
7 | import cats.MonadReader
8 |
9 | import scala.collection.mutable.{Set => MutableSet}
10 | import scala.collection.mutable.{HashSet => MutableHashSet}
11 | import scala.collection.mutable.{Map => MutableMap}
12 | import scala.collection.mutable.HashMap
13 |
14 | import org.mindrot.jbcrypt.BCrypt
15 |
16 | import scala.util.Random
17 | /*
18 | Note that in Reader, the `S` is static
19 | In `State`, `S` is intended to change.
20 | So this example does not "pass along" new copies of an immutable database
21 | */
22 |
23 | sealed trait Database {
24 |
25 | // user ID, username
26 | val users: MutableMap[Int, String]
27 |
28 | // username, (salt, hash)
29 | val passwords: MutableMap[String, (String,String)]
30 |
31 | def genId: Int
32 |
33 | override def toString: String = {
34 | val userString = users.toList.map(_.toString + '\n').mkString
35 | val pwString = passwords.toList.map(_.toString + '\n').mkString
36 |
37 | "Users: "+userString+" passwords: "+pwString
38 |
39 | }
40 | }
41 |
42 | case class TestDatabase() extends Database {
43 | // user ID, username
44 | val users: MutableMap[Int, String] = MutableMap.empty[Int,String]
45 |
46 | // username, (salt, hash)
47 | val passwords: MutableMap[String, (String, String)] =
48 | MutableMap.empty[String,(String, String)]
49 |
50 | val rand = new Random()
51 |
52 | // unsafe if all ints used...
53 | @annotation.tailrec
54 | final def genId: Int = {
55 | val r = rand.nextInt().abs
56 | if (users.contains(r))
57 | genId
58 | else
59 | r
60 | }
61 |
62 |
63 | }
64 |
65 |
66 | object DatabaseQueriesAndUpdates {
67 |
68 |
69 | // Task 2a
70 | // type DatabaseReader[A] = ???
71 |
72 | // Task 2b
73 | // def findUsername(userId: Int): ??? = ???
74 |
75 |
76 | // Task 2c
77 | // def findUserId(username: String): ??? = ???
78 |
79 | // Task 2d
80 | // def userExists(username: String): ??? = ???
81 |
82 | // Task 2e
83 | // def checkPassword(username: String, passwordClear: String): ??? = ???
84 |
85 | // Task 2f
86 | // def checkLogin(userId: Int, passwordClear: String): ??? = ???
87 |
88 |
89 | // Task 2g
90 |
91 | // def createUser(username: String, passwordClear: String): ??? = ???
92 |
93 | }
94 |
95 |
96 |
--------------------------------------------------------------------------------
/README.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | README.html
9 |
10 |
11 |
12 |
13 |
14 | Functional Scala for Data Engineers
15 | A fast-paced introduction to Scala's FP ecosystem for data engineers.
16 | These slides loosely follow Chiusano and Bjarnason's Functional Programming in Scala with an emphasis on introducing Cats and various other FP libraries. Please read chapters 1-3 before beginning.
17 | Welsh and Gurnell's Advanced Scala with Cats is an optional supplementary text, and their Essential Scala is a minimal prerequisite for the course. The first two courses in Coursera's Functional Programming in Scala Specialization are also highly recommended.
18 | Slides are viewable here .
19 |
20 | A link to ScalaDoc will be available in the slide table of contents, above.
21 | Functional Scala for Data Engineers is licensed under a Creative Commons CC0 1.0 Universal License .
22 | DS12/scala-class build status
23 |
24 | DS12/scala-class-private build status
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4/Covariance.scala:
--------------------------------------------------------------------------------
1 |
2 | package com.datascience.education.tutorial.lecture4
3 |
4 |
5 |
6 |
7 |
8 | object Covariance {
9 | sealed trait Coin
10 | case object Heads extends Coin
11 | case object Tails extends Coin
12 |
13 | // 4a
14 |
15 | sealed trait LinkedListInt
16 | case class ConsInt(h: Int, t: LinkedListInt) extends LinkedListInt
17 | case object NilInt extends LinkedListInt
18 |
19 | /*
20 | Demonstrations of generics without covariance - flawed design
21 | */
22 |
23 |
24 |
25 | // 4b
26 | sealed trait LinkedList1[A]
27 | case class Cons1[A](h: A, t: LinkedList1[A]) extends LinkedList1[A]
28 | /* bad form;
29 | a different instance of NilLL for every empty LinkedList?
30 | */
31 | case class Nil1[A]() extends LinkedList1[A]
32 |
33 | /*
34 | We want to re-use the same empty list, like Scala Collections:
35 | http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Nil$
36 | */
37 |
38 |
39 | // 4c
40 | sealed trait LinkedList2[A] {
41 | def prepend(a: A): LinkedList2[A] = Cons2(a, this)
42 | }
43 | case class Cons2[A](h: A, t: LinkedList2[A]) extends LinkedList2[A]
44 |
45 | /*
46 | Nil2 is an immutable, singleton object that will be re-used
47 | by LinkedList2's of many different types:
48 | LinkedList2[Int],
49 | LinkedList2[String],
50 | etc.
51 |
52 | A case object cannot create a generic type like a case class,
53 | explaining why this is not Nil2[A]
54 |
55 | `Nothing` is a subtype of every type,
56 | so is an apporiate concrete type to fill generic `A`
57 | in LinkedList2[A].
58 | */
59 | case object Nil2 extends LinkedList2[Nothing]
60 |
61 |
62 | // 4d
63 | sealed trait LinkedList3[+A] {
64 | def map[B](f: A => B): LinkedList3[B] = this match {
65 | case Cons3(h, t) => Cons3(f(h), t.map(f))
66 | case Nil3 => Nil3
67 | }
68 |
69 | // def prepend(a: A): LinkedList3[A] =
70 | // Cons3(a, this)
71 |
72 | }
73 | case class Cons3[+A](h: A, t: LinkedList3[A]) extends LinkedList3[A]
74 | case object Nil3 extends LinkedList3[Nothing]
75 |
76 |
77 | // 4e
78 | sealed trait LinkedList4[+A] {
79 | def map[B](f: A => B): LinkedList4[B] = this match {
80 | case Cons4(h, t) => Cons4(f(h), t.map(f))
81 | case Nil4 => Nil4
82 | }
83 |
84 | def prepend[B >: A](b: B): LinkedList4[B] =
85 | Cons4(b, this)
86 |
87 | }
88 | case class Cons4[+A](h: A, t: LinkedList4[A]) extends LinkedList4[A]
89 | case object Nil4 extends LinkedList4[Nothing]
90 |
91 |
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/slides/theme/js/squeezeFrame.js:
--------------------------------------------------------------------------------
1 | // squeezeFrame.js; standalone JS to solve small rendering issues that cause
2 | // content to be inaccessible or scrollbars to appear on iframes
3 | //
4 | // version: 201003221700
5 | //
6 | // author: frank -futtta- goossens
7 | // License: CreativeCommons Attribution-Share Alike
8 | // (http://creativecommons.org/licenses/by-sa/3.0/) or
9 | // GNU LGPL (http://www.gnu.org/licenses/lgpl-3.0.txt)
10 | // more info: http://blog.futtta.be/2010/03/24/
11 | //
12 | // usage:
13 | // * include this script in the head of the iframe-content (i.e. in the
14 | // page that is displayed inside the iframe)
15 | // * set "myContainer" to the URL of the iframe container page (i.e. the
16 | // page in which the iframe is created/ defined), to redirect users
17 | // accessing the iframe-content out of context
18 | // * optionally set "myMax" with the maximum positive/negative zoom
19 | // allowed, e.g. 0.25 means that the page can zoom between 75 to 125%,
20 | // default is 0.10, so 90->110%
21 | // * optionally set "myRedraw" to "both" (default is "width") to adjust
22 | // to both width and height
23 | //
24 | // example code:
25 | //
26 | //
31 | //
32 | // bugs & issues:
33 | // * embedded YouTube in Firefox becomes invisible (but Vimeo for example
34 | // does work)
35 | // * the vertical scrollbar does not always disappear in FF
36 | // * does not work in Opera
37 |
38 | window.onload=function() {
39 | if (self !== top) {
40 | if (typeof myMax!=="number") { max=0.1; } else { max=myMax;}
41 | if (typeof myRedraw!=="string") myRedraw="width";
42 |
43 | b=document.getElementsByTagName('body')[0];
44 |
45 | zW=(b.clientWidth-5)/b.scrollWidth;
46 |
47 | if (myRedraw==="both") {
48 | zH=(b.clientHeight)/b.scrollHeight;
49 | if (zH1+max) { z=1+max; } else if (z<1-max) { z=1-max; }
56 |
57 | s="zoom:"+z+"; -moz-transform: scale("+z+"); -moz-transform-origin: 0 0;";
58 |
59 | if (typeof b.setAttribute === "function") b.setAttribute('style', s);
60 | else if (typeof b.style.setAttribute === "object") b.style.setAttribute('cssText', s);
61 | } else {
62 | if (typeof myContainer==="string") { window.location=myContainer; }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/SafeDivision.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 |
4 | object SafeDivisionOption {
5 |
6 | def safeDivInt(numerator: Int, denominator: Int): Option[Int] =
7 | try {
8 | Some(numerator / denominator)
9 | } catch {
10 | case ae: java.lang.ArithmeticException => None
11 | }
12 |
13 | def squareRootFrac(numerator: Int, denominator: Int): Option[Double] =
14 | safeDivInt(numerator, denominator).flatMap { _ =>
15 | val squareRoot: Double =
16 | math.sqrt(numerator.toDouble / denominator)
17 | if (squareRoot.isNaN)
18 | None
19 | else
20 | Some(squareRoot)
21 | }
22 |
23 |
24 | def squareRootFrac(tup: (Int, Int)): Option[Double] =
25 | squareRootFrac(tup._1, tup._2)
26 |
27 |
28 | import TraverseOption._
29 |
30 | def traverseFractions(ll: List[(Int, Int)]): Option[List[Double]] =
31 | traverse(ll)(squareRootFrac)
32 |
33 |
34 | }
35 |
36 |
37 | object SafeDivisionOptionExamples extends App {
38 |
39 | import SafeDivisionOption._
40 |
41 |
42 | println("sqrt(7/2)")
43 | println(squareRootFrac(7,2))
44 |
45 | println("-----------------")
46 |
47 | println("sqrt(7/0)")
48 | println(squareRootFrac(7,0))
49 |
50 | println("-----------------")
51 |
52 | println("sqrt(-4/3)")
53 | println(squareRootFrac(-4,3))
54 |
55 |
56 | }
57 |
58 |
59 | import cats.data.Xor
60 | import cats.data.Xor.Left
61 | import cats.data.Xor.Right
62 |
63 | object SafeDivisionXor {
64 |
65 | // Task 2a
66 | def safeDivInt(numerator: Int, denominator: Int): Xor[Exception, Int] =
67 | ???
68 |
69 |
70 |
71 | // Task 2b
72 | def squareRootFrac(numerator: Int, denominator: Int):
73 | Xor[Exception, Double] = ???
74 |
75 |
76 | def squareRootFrac(tup: (Int, Int)): Xor[Exception, Double] =
77 | squareRootFrac(tup._1, tup._2)
78 |
79 |
80 |
81 | }
82 |
83 | object SafeDivIntXorExamples extends App {
84 | import SafeDivisionXor._
85 |
86 | println("7/2")
87 | println(safeDivInt(7,2))
88 |
89 | println("-----------------")
90 |
91 | println("7/0")
92 | println(safeDivInt(7,0))
93 |
94 |
95 | }
96 |
97 |
98 | object SquareRootFracXorExamples extends App {
99 | import SafeDivisionXor._
100 |
101 | println("sqrt(7/2)")
102 | println(squareRootFrac(7,2))
103 |
104 | println("-----------------")
105 |
106 | println("sqrt(7/0)")
107 | println(squareRootFrac(7,0))
108 |
109 | println("-----------------")
110 |
111 | println("sqrt(-4/3)")
112 | println(squareRootFrac(-4,3))
113 |
114 |
115 |
116 | }
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/XorErrors.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import cats.data.Xor
4 | import cats.data.Xor.Left
5 | import cats.data.Xor.Right
6 | import cats.data.NonEmptyList
7 |
8 |
9 | object XorErrors {
10 | import XorHelpers._
11 |
12 | type XorErrors[E, A] = Xor[NonEmptyList[E], A]
13 |
14 | // use Right and Left instead
15 | // def right[E,A](a: A): XorErrors[Nothing, A] = Right(a)
16 | // def left[E](e: E): XorErrors[E, Nothing] = Left(NonEmptyList(e))
17 |
18 |
19 | // think of map2 as an `And` operation
20 | def map2[E, A, B, C](xorErrors1: XorErrors[E,A], xorErrors2: XorErrors[E,B])
21 | (f: Function2[A, B, C]): XorErrors[E,C] =
22 | (xorErrors1, xorErrors2) match {
23 | case (Right(a1), Right(b2)) => Right(f(a1,b2))
24 | case (Left(e1), Right(b2)) => Left(e1)
25 | case (Right(a1), Left(e2)) => Left(e2)
26 | case (Left(e1), Left(e2)) => Left(concat(e1, e2))
27 | }
28 |
29 | // not really good form
30 | def concatErrors[E]
31 | (xorErrors1: XorErrors[E,Nothing], xorErrors2: XorErrors[E,Nothing]):
32 | XorErrors[E,Nothing] = (xorErrors1, xorErrors2) match {
33 | case (Left(e1), Left(e2)) => Left(concat(e1, e2))
34 | }
35 |
36 |
37 | def map4[Err, A, B, C, D, E](xorErrors1: XorErrors[Err, A],
38 | xorErrors2: XorErrors[Err, B],
39 | xorErrors3: XorErrors[Err, C],
40 | xorErrors4: XorErrors[Err, D])
41 | (f: Function4[A, B, C, D, E]): XorErrors[Err, E] = {
42 |
43 | val xorAB: XorErrors[Err, Tuple2[A,B]] = map2(xorErrors1, xorErrors2)((_,_))
44 | val xorCD: XorErrors[Err, Tuple2[C,D]] = map2(xorErrors3, xorErrors4)((_,_))
45 |
46 | val g: Function2[Tuple2[A, B], Tuple2[C, D], E] =
47 | (tupAB: Tuple2[A,B], tupCD: Tuple2[C,D]) => {
48 | val a: A = tupAB._1
49 | val b: B = tupAB._2
50 | val c: C = tupCD._1
51 | val d: D = tupCD._2
52 | f(a,b,c,d): E
53 | }
54 |
55 |
56 | val xorE: XorErrors[Err, E] = map2(xorAB, xorCD)(g)
57 |
58 | xorE
59 | }
60 |
61 |
62 | def and[E, A](xorErrors1: XorErrors[E,A], xorErrors2: XorErrors[E,A]): XorErrors[E,A] =
63 | (xorErrors1, xorErrors2) match {
64 | case (Right(a1), Right(a2)) => Right(a1)
65 | case (Left(e1), Right(a2)) => Left(e1)
66 | case (Right(a1), Left(e2)) => Left(e2)
67 | case (Left(e1), Left(e2)) => Left(concat(e1, e2))
68 | }
69 |
70 | def or[E, A](xorErrors1: XorErrors[E,A], xorErrors2: XorErrors[E,A]): XorErrors[E,A] =
71 | (xorErrors1, xorErrors2) match {
72 | case (Right(a1), Right(a2)) => Right(a1)
73 | case (Left(e1), Right(a2)) => Right(a2) // information lost about Left error
74 | case (Right(a1), Left(e2)) => Right(a1) // information lost about Right error
75 | case (Left(e1), Left(e2)) => Left(concat(e1, e2))
76 | }
77 |
78 |
79 |
80 |
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/project/Dependencies.scala:
--------------------------------------------------------------------------------
1 | // taken from https://github.com/TrueCar/mleap/blob/master/project/Dependencies.scala
2 |
3 | import sbt._
4 |
5 | object Dependencies {
6 | import Common.scalaVer
7 |
8 |
9 | val catsVersion = "0.6.0"
10 | val dogsVersion = "0.2.2"
11 | val algebirdVersion = "0.12.1"
12 | val shapelessVersion = "2.3.2"
13 | val framelessVersion = "0.1.0"
14 | val summingbirdVersion = "0.11.0-RC1"
15 | val breezeVersion = "0.12"
16 | val spireVersion = "0.12.0"
17 | val json4sVersion = "3.4.0"
18 |
19 | val scalatestVersion = "3.0.0"
20 |
21 | // will use Spark 2.0 and Scala 2.11
22 | // Spark 1.6, Scala 2.10 could be delicately joined with the rest of the 2.11 repo
23 | // Fortunately this is no longer necessary
24 | //val sparkVer = "1.6.2"
25 |
26 | val kindProjector = compilerPlugin("org.spire-math" % "kind-projector" % "0.8.0" cross CrossVersion.binary)
27 | val si2712 = compilerPlugin("com.milessabin" % "si2712fix-plugin" % "1.2.0" cross CrossVersion.full)
28 |
29 | lazy val dependencies = Seq(
30 | "org.typelevel" %% "cats" % catsVersion,
31 | "org.typelevel" %% "dogs-core" % dogsVersion,
32 | "com.twitter" %% "algebird-core" % algebirdVersion,
33 | "com.twitter" %% "algebird-test" % algebirdVersion,
34 | "com.twitter" %% "algebird-util" % algebirdVersion,
35 | "com.twitter" %% "algebird-bijection" % algebirdVersion,
36 | "com.chuusai" %% "shapeless" % shapelessVersion,
37 | "io.github.adelbertc" %% "frameless-cats" % framelessVersion,
38 | "io.github.adelbertc" %% "frameless-dataset" % framelessVersion,
39 | "io.github.adelbertc" %% "frameless-dataframe" % framelessVersion,
40 | "org.scalatest" %% "scalatest" % scalatestVersion % "test",
41 | "org.scalacheck" % "scalacheck_2.11" % "1.13.2" % "test",
42 | "org.scalanlp" %% "breeze" % breezeVersion,
43 | "org.scalanlp" %% "breeze-natives" % breezeVersion,
44 | "org.scalanlp" %% "breeze-viz" % breezeVersion,
45 | "org.spire-math" %% "spire" % spireVersion,
46 | "de.svenkubiak" % "jBCrypt" % "0.4.1",
47 | "com.twitter" %% "summingbird" % summingbirdVersion,
48 | "com.twitter" %% "summingbird-example" % summingbirdVersion,
49 | "com.twitter" %% "summingbird-core" % summingbirdVersion,
50 | "com.twitter" %% "summingbird-client" % summingbirdVersion,
51 | "com.twitter" %% "summingbird-chill" % summingbirdVersion,
52 | "com.twitter" %% "summingbird-batch" % summingbirdVersion,
53 | "com.twitter" %% "summingbird-builder" % summingbirdVersion,
54 | "com.twitter" %% "summingbird-storm" % summingbirdVersion,
55 | "org.json4s" %% "json4s-native" % json4sVersion,
56 | "commons-validator" % "commons-validator" % "1.5.1",
57 | kindProjector,
58 | si2712
59 | )
60 |
61 |
62 | // lazy val sparkDependencies = Seq(
63 | // "org.apache.spark" %% "spark-core" % sparkVer % "provided",
64 | // "org.apache.spark" %% "spark-sql" % sparkVer % "provided").union(dependencies)
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/RequestResponse.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import scala.util.Random
4 |
5 | import cats.data.Xor
6 | import cats.data.Xor.Left
7 | import cats.data.Xor.Right
8 |
9 | import java.lang.{ String => JString }
10 |
11 | object RequestResponse {
12 |
13 | val rand = new Random
14 |
15 | def randString: String =
16 | rand.alphanumeric.take(8).toList.foldLeft("")(_ + _)
17 |
18 |
19 | trait ClientException extends Exception {
20 | def message: String
21 | def cause: Throwable
22 | }
23 |
24 |
25 | class BadRequestException(val message: String = null, val cause: Throwable = null)
26 | extends ClientException
27 |
28 | class CorruptPayloadException(val message: String = null, val cause: Throwable = null)
29 | extends ClientException
30 |
31 |
32 | case class Request(req: String)
33 | case class Response(arr: Array[Byte])
34 | case class Payload(pay: String)
35 |
36 |
37 | def sendRequestUnsafe(request: Request): Response =
38 | if (rand.nextDouble() < 0.02)
39 | throw new BadRequestException(s"No response received for request: $request")
40 | else {
41 | val bytes = { request.req }.getBytes()
42 |
43 | Response(bytes)
44 | }
45 |
46 | def unpackResponseUnsafe(
47 | response: Response, outOfMemory: Boolean = false): Payload = {
48 |
49 | if(outOfMemory)
50 | throw new Exception("out of memory")
51 |
52 |
53 | if (rand.nextDouble() < 0.02)
54 | throw new CorruptPayloadException(s"Payload of response corrupted: $response")
55 | else {
56 | val pay = response.arr
57 | Payload(new JString(response.arr))
58 | }
59 |
60 | }
61 | def clientUnsafe(request: Request): Payload =
62 | unpackResponseUnsafe(sendRequestUnsafe(request))
63 |
64 |
65 | // Task 3a
66 | def sendRequest(request: Request):
67 | Xor[BadRequestException, Response] =
68 | ???
69 |
70 | // Task 3b
71 | def unpackResponse(response: Response)(outOfMemory: Boolean):
72 | Xor[CorruptPayloadException, Payload] =
73 | ???
74 |
75 | // Task 3c
76 | def client(request: Request, outOfMemory: Boolean = false):
77 | Xor[ClientException, Payload] =
78 | ???
79 |
80 |
81 | }
82 |
83 | object RequestResponseExample extends App {
84 | import RequestResponse._
85 |
86 | val requests = (1 to 8).map(_ => Request(randString))
87 |
88 | requests.foreach { request =>
89 | val payload: Xor[Throwable, Payload] = client(request)
90 | println("------------------")
91 | println(s"sending request: $request")
92 | println(payload)
93 | }
94 |
95 |
96 | println("-------------------------------")
97 | println("Catastrophic exception should not be caught")
98 |
99 | // Task 3d
100 | // requests.foreach { request =>
101 | // val payload: Xor[Throwable, Payload] = client(request, true)
102 | // println("------------------")
103 | // println(s"sending request: $request")
104 | // println(payload)
105 | // }
106 |
107 |
108 | }
109 |
110 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/TraverseOption.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import scala.Option
4 | import scala.Some
5 | import scala.None
6 |
7 | import cats.Applicative
8 | import cats.std.list._
9 |
10 | /*
11 | `traverse` and `sequence` from Chapter 4,
12 | implemented on Scala Collections' `Option`
13 |
14 | */
15 |
16 | object TraverseOption {
17 | /*
18 | `traverse2` and `sequence2` are implemented from scratch (`sequence2` uses `traverse2`)
19 | `traverse` and `sequence` are implemented on top of Cats.
20 | It is preferable to use the implementation of `traverse`
21 | provided by Cats.
22 |
23 | Cost of Cats implementation
24 | Cats' `traverse` implementation obfuscates the solution to our challenge
25 |
26 | Benefit of Cats implementation
27 | `traverse` as implemented in FPiS Chapter 4 is simplified.
28 | (A => Option[B]) => Option[List[B]] *really* should be a combinator on
29 | `List`, not `Option`.
30 |
31 | Cats and Scalaz both implement it this way.
32 |
33 | FPiS' implementation of `traverse` for both Option and Either is simplified
34 |
35 | */
36 |
37 | /*
38 | Implement `prepend`, a helper function for `traverse2`
39 | */
40 | private def prepend[A](opA: Option[A], opListA: Option[List[A]]): Option[List[A]] =
41 | opA.flatMap { (a: A) =>
42 | opListA.map { (listA: List[A]) => a :: listA }
43 | }
44 |
45 | /*
46 | Implement `traverse2` using `prepend`. Do not use `sequence2`.
47 | */
48 | def traverse2[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
49 | a.foldRight[Option[List[B]]](Some(List[B]())) { (a: A, opListB: Option[List[B]]) =>
50 | val opB: Option[B] = f(a)
51 | prepend(opB, opListB)
52 | }
53 |
54 | /*
55 | Implement `sequence2` using `traverse2`.
56 | The body of this method is a one-liner.
57 | */
58 | def sequence2[A, B](listOpA: List[Option[A]]): Option[List[A]] =
59 | traverse2(listOpA) { (opA: Option[A]) => opA }
60 |
61 | /*
62 | Requirement for production-ready implementation of `traverse` and `sequence` below.
63 | */
64 | implicit val applicativeOption = new Applicative[Option] {
65 | def pure[A](a: A): Option[A] = Some(a)
66 | // Members declared in cats.Apply
67 | def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] =
68 | (ff, fa) match {
69 | case (Some(ab), Some(a)) => Some(ab(a))
70 | case _ => None
71 | }
72 | // Members declared in cats.Cartesian
73 | override def product[A, B](fa: Option[A], fb: Option[B]): Option[(A, B)] =
74 | (fa, fb) match {
75 | case (Some(a), Some(b)) => Some((a, b))
76 | case _ => None
77 | }
78 | // Members declared in cats.Functor
79 | override def map[A, B](fa: Option[A])(f: A => B): Option[B] =
80 | fa.map(f)
81 | }
82 |
83 | /*
84 | Here, G = Option, and Applicative[G] = Applicative[Option]
85 | */
86 | def traverse[A, B](listA: List[A])(f: A => Option[B]): Option[List[B]] =
87 | listInstance.traverse(listA)(f)(applicativeOption)
88 |
89 | def sequence[A](loa: List[Option[A]]): Option[List[A]] =
90 | listInstance.traverse(loa) { (oa: Option[A]) => oa }(applicativeOption)
91 |
92 | }
93 |
94 |
95 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/XorWebForm.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import cats.data.Xor
4 | import cats.data.Xor.Left
5 | import cats.data.Xor.Right
6 | import cats.data.NonEmptyList
7 | import org.apache.commons.validator.routines.EmailValidator
8 | import XorErrors._
9 |
10 | import scala.language.higherKinds
11 |
12 | object XorWebFormVerifier
13 | extends WebFormVerifier[XorErrors] {
14 |
15 | //http://stackoverflow.com/a/19754142/1007926
16 | val alpha = (('a' to 'z') ++ ('A' to 'Z')).toSet
17 |
18 | def isAlpha(string: String): XorErrors[String, String] =
19 | ???
20 |
21 | def minimumLength(minLength: Int)(string: String): XorErrors[String, String] =
22 | ???
23 |
24 | /*
25 | This version of `validateName` runs two checks on `name: String` --
26 | the string must only contain alphabetic characters,
27 | and the string must be at least 3 characters long.
28 |
29 | Here, "failing fast" means if `isAlpha` fails,
30 |
31 |
32 | def verifyName(name: String): XorErrors[String, String] =
33 | isAlpha(name).flatMap { alphaName =>
34 | minimumLength(3)(alphaName)
35 | }
36 | */
37 |
38 | /*
39 | For first and last name
40 | */
41 | def verifyName(name: String): XorErrors[String, String] = {
42 | ???
43 | }
44 |
45 |
46 | // http://stackoverflow.com/a/19590634/1007926
47 | def numDigits(num: Long, length: Int): XorErrors[String, Long] =
48 | ???
49 |
50 | def verifyPhoneNumber(phoneNumber: Long): XorErrors[String, Long] = {
51 | ???
52 | }
53 |
54 | /*
55 | Email address validation
56 | " What is the best Java email address validation method? "
57 | http://stackoverflow.com/a/624590/1007926
58 | https://commons.apache.org/proper/commons-validator/apidocs/org/apache/commons/validator/routines/EmailValidator.html
59 | */
60 |
61 | val validator: EmailValidator = EmailValidator.getInstance()
62 |
63 | def validEmailAddress(emailAddress: String): Boolean = ???
64 |
65 | def verifyEmailAddress(emailAddress: String): XorErrors[String, String] = {
66 | ???
67 | }
68 |
69 |
70 |
71 | def verify(unverifiedWebForm: UnverifiedWebForm):
72 | XorErrors[String, VerifiedWebForm] = {
73 | ???
74 | }
75 | }
76 |
77 |
78 |
79 | object XorWebFormVerifierExample extends App {
80 |
81 | import XorWebFormVerifier._
82 |
83 | val unverified = UnverifiedWebForm("P3t3r", "Bec1ch", 1234, "no@suffix")
84 |
85 | val verified = verify(unverified)
86 |
87 | println("unverified: ")
88 | println(unverified)
89 |
90 | println("result of verification: ")
91 | println(verified)
92 |
93 | println("--------------------")
94 |
95 | val unverified2 = UnverifiedWebForm("P3t3r", "Bec1ch", 1234567890, "no@suffix")
96 |
97 | val verified2 = verify(unverified2)
98 |
99 | println("unverified: ")
100 | println(unverified2)
101 |
102 | println("result of verification: ")
103 | println(verified2)
104 |
105 | println("--------------------")
106 |
107 | val unverified3 = UnverifiedWebForm("Peter", "Becich", 1234567890, "peter@datascience.com")
108 | println("unverified: ")
109 | println(unverified3)
110 |
111 | val verified3 = verify(unverified3)
112 |
113 | println("result of verification: ")
114 | println(verified3)
115 |
116 |
117 |
118 |
119 | }
120 |
121 |
122 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Scala by DS12
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
30 |
31 |
32 | Lecture Slides
33 |
34 |
108 |
109 | Lectures 4-13a are numbered according to their corresponding chapter in Functional Programming in Scala .
110 |
111 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/stylesheets/github-light.css:
--------------------------------------------------------------------------------
1 | /*
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2016 GitHub, Inc.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | */
25 |
26 | .pl-c /* comment */ {
27 | color: #969896;
28 | }
29 |
30 | .pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */,
31 | .pl-s .pl-v /* string variable */ {
32 | color: #0086b3;
33 | }
34 |
35 | .pl-e /* entity */,
36 | .pl-en /* entity.name */ {
37 | color: #795da3;
38 | }
39 |
40 | .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */,
41 | .pl-s .pl-s1 /* string source */ {
42 | color: #333;
43 | }
44 |
45 | .pl-ent /* entity.name.tag */ {
46 | color: #63a35c;
47 | }
48 |
49 | .pl-k /* keyword, storage, storage.type */ {
50 | color: #a71d5d;
51 | }
52 |
53 | .pl-s /* string */,
54 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */,
55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
56 | .pl-sr /* string.regexp */,
57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */,
58 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */,
59 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ {
60 | color: #183691;
61 | }
62 |
63 | .pl-v /* variable */ {
64 | color: #ed6a43;
65 | }
66 |
67 | .pl-id /* invalid.deprecated */ {
68 | color: #b52a1d;
69 | }
70 |
71 | .pl-ii /* invalid.illegal */ {
72 | color: #f8f8f8;
73 | background-color: #b52a1d;
74 | }
75 |
76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ {
77 | font-weight: bold;
78 | color: #63a35c;
79 | }
80 |
81 | .pl-ml /* markup.list */ {
82 | color: #693a17;
83 | }
84 |
85 | .pl-mh /* markup.heading */,
86 | .pl-mh .pl-en /* markup.heading entity.name */,
87 | .pl-ms /* meta.separator */ {
88 | font-weight: bold;
89 | color: #1d3e81;
90 | }
91 |
92 | .pl-mq /* markup.quote */ {
93 | color: #008080;
94 | }
95 |
96 | .pl-mi /* markup.italic */ {
97 | font-style: italic;
98 | color: #333;
99 | }
100 |
101 | .pl-mb /* markup.bold */ {
102 | font-weight: bold;
103 | color: #333;
104 | }
105 |
106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ {
107 | color: #bd2c00;
108 | background-color: #ffecec;
109 | }
110 |
111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
112 | color: #55a532;
113 | background-color: #eaffea;
114 | }
115 |
116 | .pl-mdr /* meta.diff.range */ {
117 | font-weight: bold;
118 | color: #795da3;
119 | }
120 |
121 | .pl-mo /* meta.output */ {
122 | color: #1d3e81;
123 | }
124 |
125 |
--------------------------------------------------------------------------------
/docs/stylesheets/github-light.css:
--------------------------------------------------------------------------------
1 | /*
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2016 GitHub, Inc.
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
24 | */
25 |
26 | .pl-c /* comment */ {
27 | color: #969896;
28 | }
29 |
30 | .pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */,
31 | .pl-s .pl-v /* string variable */ {
32 | color: #0086b3;
33 | }
34 |
35 | .pl-e /* entity */,
36 | .pl-en /* entity.name */ {
37 | color: #795da3;
38 | }
39 |
40 | .pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */,
41 | .pl-s .pl-s1 /* string source */ {
42 | color: #333;
43 | }
44 |
45 | .pl-ent /* entity.name.tag */ {
46 | color: #63a35c;
47 | }
48 |
49 | .pl-k /* keyword, storage, storage.type */ {
50 | color: #a71d5d;
51 | }
52 |
53 | .pl-s /* string */,
54 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */,
55 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
56 | .pl-sr /* string.regexp */,
57 | .pl-sr .pl-cce /* string.regexp constant.character.escape */,
58 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */,
59 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ {
60 | color: #183691;
61 | }
62 |
63 | .pl-v /* variable */ {
64 | color: #ed6a43;
65 | }
66 |
67 | .pl-id /* invalid.deprecated */ {
68 | color: #b52a1d;
69 | }
70 |
71 | .pl-ii /* invalid.illegal */ {
72 | color: #f8f8f8;
73 | background-color: #b52a1d;
74 | }
75 |
76 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ {
77 | font-weight: bold;
78 | color: #63a35c;
79 | }
80 |
81 | .pl-ml /* markup.list */ {
82 | color: #693a17;
83 | }
84 |
85 | .pl-mh /* markup.heading */,
86 | .pl-mh .pl-en /* markup.heading entity.name */,
87 | .pl-ms /* meta.separator */ {
88 | font-weight: bold;
89 | color: #1d3e81;
90 | }
91 |
92 | .pl-mq /* markup.quote */ {
93 | color: #008080;
94 | }
95 |
96 | .pl-mi /* markup.italic */ {
97 | font-style: italic;
98 | color: #333;
99 | }
100 |
101 | .pl-mb /* markup.bold */ {
102 | font-weight: bold;
103 | color: #333;
104 | }
105 |
106 | .pl-md /* markup.deleted, meta.diff.header.from-file */ {
107 | color: #bd2c00;
108 | background-color: #ffecec;
109 | }
110 |
111 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
112 | color: #55a532;
113 | background-color: #eaffea;
114 | }
115 |
116 | .pl-mdr /* meta.diff.range */ {
117 | font-weight: bold;
118 | color: #795da3;
119 | }
120 |
121 | .pl-mo /* meta.output */ {
122 | color: #1d3e81;
123 | }
124 |
125 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4a/TraverseXor.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4a
2 |
3 | import cats.data.Xor
4 | import cats.data.Xor.Left
5 | import cats.data.Xor.Right
6 |
7 | import cats.Applicative
8 | import cats.std.list._
9 |
10 | import cats.data.NonEmptyList
11 |
12 | object TraverseXor {
13 |
14 | /*
15 | See note at top of `TraverseOption`, regarding why we should not use
16 | `traverse` method built into `Xor`:
17 | http://typelevel.org/cats/api/index.html#cats.data.Xor@traverse[F[_],AA%3E:A,D]%28f:B=%3EF[D]%29%28implicitF:cats.Applicative[F]%29:F[cats.data.Xor[AA,D]]
18 |
19 |
20 | Note the signature of `Xor.traverse`:
21 | given Xor[E, A],
22 | and F[_] = List[B]
23 | the signature of `traverse` is:
24 | (A => List[B]): List[Xor[E, B]]
25 |
26 | This is the inverse of what we want --- Xor[List[E, B]]
27 |
28 | This means that the `traverse` function we seek belongs to List.
29 | The return type of this sought-after `traverse` function is
30 | Xor[List[E, B]]. This matches up with `traverse` implemented on FPiS'
31 | `Either`.
32 |
33 | Scala Collections' `List` does not provide `traverse`.
34 |
35 | Cats' `Applicative` does.
36 |
37 | */
38 |
39 | /*
40 | `XorException` is the same trick introduced in chapter 12 of FP in Scala.
41 |
42 | The type inside Applicative may only have one generic (like Option),
43 | but Xor has two.
44 | So we must remove one generic from Xor. XorException is more
45 | limited than Xor because `Exception` is "hard-wired" into it.
46 | */
47 | type XorException[B] = Xor[Exception, B]
48 | type XorListException[B] = Xor[NonEmptyList[Exception], B]
49 |
50 | implicit val applicativeXorException = new Applicative[XorException] {
51 | def pure[A](a: A): XorException[A] = Right(a)
52 |
53 | // Members declared in cats.Apply
54 | def ap[A, B](ff: XorException[A => B])(fa: XorException[A]): XorException[B] =
55 | (ff, fa) match {
56 | case (Right(ab), Right(a)) => Right(ab(a))
57 | case (left1 @ Left(_), _) => left1: XorException[B]
58 | case (_, left2 @ Left(_)) => left2: XorException[B]
59 | }
60 |
61 | // Members declared in cats.Cartesian
62 | override def product[A, B](fa: XorException[A], fb: XorException[B]): XorException[(A, B)] =
63 | (fa, fb) match {
64 | case (Right(a), Right(b)) => Right((a, b))
65 | case (left1 @ Left(_), _) => left1: XorException[(A, B)]
66 | case (_, left2 @ Left(_)) => left2: XorException[(A, B)]
67 | }
68 |
69 | // Members declared in cats.Functor
70 | override def map[A, B](fa: XorException[A])(f: A => B): XorException[B] =
71 | fa.map(f)
72 |
73 | }
74 |
75 | def traverse[A, B](listA: List[A])(f: A => XorException[B]): XorException[List[B]] =
76 | listInstance.traverse(listA)(f)(applicativeXorException)
77 |
78 | def sequence[A](lxa: List[XorException[A]]): XorException[List[A]] =
79 | listInstance.traverse(lxa) { (xa: XorException[A]) => xa }(applicativeXorException)
80 |
81 | implicit val applicativeXorListException = new Applicative[XorListException] {
82 | def pure[A](a: A): XorListException[A] = Right(a)
83 |
84 | // Members declared in cats.Apply
85 | def ap[A, B](ff: XorListException[A => B])(fa: XorListException[A]): XorListException[B] =
86 | (ff, fa) match {
87 | case (Right(ab), Right(a)) => Right(ab(a))
88 | case (left1 @ Left(_), _) => left1: XorListException[B]
89 | case (_, left2 @ Left(_)) => left2: XorListException[B]
90 | }
91 |
92 | // Members declared in cats.Cartesian
93 | override def product[A, B](fa: XorListException[A], fb: XorListException[B]): XorListException[(A, B)] =
94 | (fa, fb) match {
95 | case (Right(a), Right(b)) => Right((a, b))
96 | case (left1 @ Left(_), _) => left1: XorListException[(A, B)]
97 | case (_, left2 @ Left(_)) => left2: XorListException[(A, B)]
98 | }
99 |
100 | // Members declared in cats.Functor
101 | override def map[A, B](fa: XorListException[A])(f: A => B): XorListException[B] =
102 | fa.map(f)
103 |
104 | }
105 |
106 | def traverse2[A, B](listA: List[A])(f: A => XorListException[B]):
107 | XorListException[List[B]] =
108 | listInstance.traverse(listA)(f)(applicativeXorListException)
109 |
110 | def sequence2[A](lxa: List[XorListException[A]]):
111 | XorListException[List[A]] =
112 | listInstance.traverse(lxa) { (xa: XorListException[A]) =>
113 | xa }(applicativeXorListException)
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4/FPOption.scala:
--------------------------------------------------------------------------------
1 |
2 | package com.datascience.education.tutorial.lecture4
3 |
4 |
5 |
6 | // Hide Scala's native Option, Some, and None from this namespace
7 | import scala.{ Option => _ }
8 | import scala.{ Some => _ }
9 | import scala.{ None => _ }
10 |
11 | // Part (5b)
12 |
13 | sealed trait FPOption[A] {
14 | import FPOption._
15 |
16 | // def map[B](f: A => B): FPOption[B] = this match {
17 | // case FPSome(a) => FPSome(f(a))
18 | // case FPNone => FPNone
19 | // }
20 |
21 | // Part (5b)
22 | // def getOrElse(default: => A): A = this match {
23 | // case FPSome(a) => a
24 | // case FPNone => default
25 | // }
26 |
27 |
28 |
29 |
30 | // Task 5c
31 | // def orElse[A](opA: FPOption[A]): FPOption[A] =
32 | // ???
33 |
34 |
35 | // Task 5d
36 | // def flatMap[B](f: A => FPOption[B]): FPOption[B] =
37 | // ???
38 |
39 |
40 | // Task 5e
41 | // def map2[B, C](opB: FPOption[B])(f: (A,B) => C): FPOption[C] =
42 | // ???
43 |
44 |
45 | }
46 |
47 |
48 | object FPOption {
49 |
50 | case class FPSome[A](get: A) extends FPOption[A]
51 | case object FPNone extends FPOption[Nothing]
52 |
53 | def unit[A](a: A): FPOption[A] = FPSome(a)
54 |
55 | }
56 |
57 |
58 |
59 | object FPOptionExamples5b extends App {
60 | // imports all members of the `FPOption` companion object
61 | import FPOption._
62 |
63 | // val optionHello: FPOption[String] = FPSome("hello")
64 |
65 | // val option65: FPOption[Int] = FPSome(65)
66 | // println(s"FPOption[Int] = $option65")
67 | // val optionChar: FPOption[Char] = option65.map((i: Int) => i.toChar)
68 | // println(s"mapped to FPOption[Char] = $optionChar")
69 |
70 | // println("---------------------")
71 | // val noInt: FPOption[Int] = FPNone
72 | // println(s"FPOption[Int] = $noInt")
73 | // val noChar: FPOption[Char] = noInt.map((i: Int) => i.toChar)
74 | // println(s"mapped to FPOption[Char] = $noChar")
75 |
76 | // println("---------------------")
77 |
78 | // def capitalLetter(i: Int): FPOption[Char] =
79 | // if (i >= 65 && i <= 90) FPSome(i.toChar) // use unit here
80 | // else FPNone
81 |
82 | // println(s"FPOption[Int] = $noInt")
83 | // val orElseSentinel = noInt.getOrElse(999)
84 | // println(s"getOrElse; replaced with sentinel value: $orElseSentinel")
85 |
86 |
87 |
88 |
89 |
90 | }
91 |
92 |
93 | // object FPOptionExamples5c extends App {
94 |
95 |
96 | // // imports all members of the `FPOption` companion object
97 | // import FPOption._
98 |
99 | // val optionHello: FPOption[String] = FPSome("hello")
100 | // val option65: FPOption[Int] = FPSome(65)
101 | // val noInt: FPOption[Int] = FPNone
102 | // val option64: FPOption[Int] = unit(64) // alternative construction
103 |
104 | // def capitalLetter(i: Int): FPOption[Char] =
105 | // if (i >= 65 && i <= 90) FPSome(i.toChar) // use unit here
106 | // else FPNone
107 |
108 |
109 | // println(s"FPOption[Int] = $option65")
110 | // val opAny1: FPOption[Any] = option65.orElse(optionHello)
111 | // println(s"orElse; String as a supertype of Int; inferred to Any: "+opAny1)
112 |
113 |
114 | // println("---------------------")
115 |
116 | // println(s"FPOption[Int] = $noInt")
117 | // val opAny2 = noInt.orElse(optionHello)
118 | // println(s"orElse; String as a supertype of Int; inferred to Any: "+opAny2)
119 |
120 | // println("---------------------")
121 | // println(s"FPOption[Int] = $noInt")
122 | // val orElse64 = noInt.orElse(option64)
123 | // println(s"orElse; replaced with 64: $orElse64")
124 |
125 |
126 |
127 | // }
128 |
129 |
130 |
131 |
132 | // object FPOptionExamples5d extends App {
133 |
134 |
135 | // // imports all members of the `FPOption` companion object
136 | // import FPOption._
137 |
138 | // val optionHello: FPOption[String] = FPSome("hello")
139 | // val option65: FPOption[Int] = FPSome(65)
140 | // val noInt: FPOption[Int] = FPNone
141 |
142 | // def capitalLetter(i: Int): FPOption[Char] =
143 | // if (i >= 65 && i <= 90) FPSome(i.toChar) // use unit here
144 | // else FPNone
145 |
146 |
147 |
148 | // println(s"FPOption[Int] = $option65")
149 | // val optionChar2: FPOption[Char] = option65.flatMap(capitalLetter)
150 | // println(s"flatMapped to FPOption[Char] = $optionChar2")
151 |
152 | // println("---------------------")
153 | // val option64: FPOption[Int] = unit(64) // alternative construction
154 | // println(s"FPOption[Int] = $option64")
155 |
156 | // val optionChar3: FPOption[Char] = option64.flatMap(capitalLetter)
157 | // println(s"flatMapped to FPOption[Char] = $optionChar3")
158 |
159 | // println("---------------------")
160 | // println(s"FPOption[Int] = $noInt")
161 | // val optionChar4: FPOption[Char] = noInt.flatMap(capitalLetter)
162 | // println(s"flatMapped to FPOption[Char] = $optionChar4")
163 |
164 |
165 |
166 | // }
167 |
168 |
169 | // object FPOptionExamples5e extends App {
170 |
171 |
172 | // // imports all members of the `FPOption` companion object
173 | // import FPOption._
174 |
175 | // val option64: FPOption[Int] = FPSome(64)
176 | // val option65: FPOption[Int] = FPSome(65)
177 | // val noInt: FPOption[Int] = FPNone
178 |
179 | // val s = option64.map2(option65)(_+_)
180 | // println("the sum is "+s)
181 |
182 |
183 | // val noSum = option64.map2(noInt)(_+_)
184 | // println("no sum: "+noSum)
185 |
186 |
187 |
188 | // }
189 |
190 |
191 |
--------------------------------------------------------------------------------
/sbtTutorial/usingCats/src/main/scala/UserRepository.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.storage
2 |
3 | import scala.language.higherKinds
4 | import scala.util.Try
5 | import cats._
6 | import cats.std.all._
7 | import cats.syntax.functor._
8 | import cats.data.Kleisli
9 |
10 |
11 | /*
12 |
13 | This is a warm-up for Monads Week.
14 | You will see similar examples in lectures 11 and 12.
15 | The topic of this particular example is dependency injection with the Reader Monad.
16 |
17 | The example this is based upon hides a lot in its imports. I think this version is a little more explicit with its use of the Reader Monad.
18 | http://eed3si9n.com/herding-cats/Reader.html
19 |
20 |
21 | */
22 | object UserExperiment {
23 |
24 | case class User(id: Long, bossId: Long, name: String)
25 |
26 | trait UserRepo {
27 | // def users: List[User]
28 | def names: List[String]
29 | def get(id: Long): User
30 | def find(name: String): User
31 | }
32 |
33 | /*
34 | Later, handle context of failure -- user not in repo
35 | */
36 |
37 | type Id[A] = A
38 | def identity[A](a: A): Id[A] = a
39 |
40 | val FlatMapId = new FlatMap[Id] {
41 | def flatMap[A, B](fa: Id[A])(f: A => Id[B]): Id[B] =
42 | f(fa)
43 |
44 | def map[A, B](fa: Id[A])(f: A => B): Id[B] =
45 | f(fa)
46 | }
47 |
48 | type UserRepoReader[A] = Kleisli[Id, UserRepo, A]
49 |
50 | def getUser(id: Long): UserRepoReader[User] =
51 | new Kleisli( (repo: UserRepo) =>
52 | identity(repo.get(id))
53 | )
54 |
55 | def findUser(name: String): UserRepoReader[User] =
56 | new Kleisli( (repo: UserRepo) =>
57 | identity(repo.find(name))
58 | )
59 |
60 | /*
61 | Generalized in `userSummarizer`.
62 | Add a context, like the user not existing in the Repo.
63 | Option handles this context.
64 | */
65 | // def userSummary(name: String): UserRepoReader[String] =
66 | // findUser(name).flatMap { user =>
67 | // getUser(user.bossId).map { boss =>
68 | // s"${user.name}'s boss is ${boss.name}"
69 | // }
70 | // }
71 |
72 |
73 |
74 | // def summarizeAllUsers: UserRepoReader[List[String]] =
75 | // new Kleisli( (repo: UserRepo) => {
76 | // val names: List[String] = repo.names
77 | // val summaries: List[UserRepoReader[String]] =
78 | // names.map { name => userSummary(name) }
79 | // this.traverse(summaries)
80 | // }
81 | // )
82 |
83 |
84 | // val idToOption = new Naturan
85 |
86 | val applicativeOption: Applicative[Option] = new Applicative[Option] {
87 | // depends on Option's Monad but that's okay for now
88 | def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] =
89 | ff.flatMap { ab =>
90 | fa.map { a => ab(a) }
91 | }
92 | def pure[A](x: A): Option[A] = Some(x)
93 | }
94 |
95 | val FlatMapOption = new FlatMap[Option] {
96 | def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] =
97 | fa.flatMap(f)
98 |
99 | def map[A, B](fa: Option[A])(f: A => B): Option[B] =
100 | fa.map(f)
101 | }
102 |
103 | /*
104 | Without altering UserRepo, which is unsafe,
105 | lets modify the Reader to handle exceptions thrown by UserRepo.
106 |
107 | We'll lift Try/Option into the Reader
108 | */
109 |
110 | type UserRepoReaderOption[A] = Kleisli[Option, UserRepo, A]
111 |
112 |
113 | // def userSummarySafe(name: String): UserRepoReaderOption[String] =
114 | // userSummary(name).lift(applicativeOption)
115 |
116 |
117 | def getUserSafe(id: Long): UserRepoReaderOption[User] =
118 | new Kleisli( (repo: UserRepo) =>
119 | Try(repo.get(id)).toOption
120 | )
121 |
122 | def findUserSafe(name: String): UserRepoReaderOption[User] =
123 | new Kleisli( (repo: UserRepo) =>
124 | Try(repo.find(name)).toOption
125 | )
126 |
127 |
128 | /*
129 | There is probably a better way to add context to our original
130 | UserRepoReader, but this is a start.
131 | */
132 | def userSummarizer[F[_]](
133 | getUserMethod: Long => Kleisli[F, UserRepo, User],
134 | findUserMethod: String => Kleisli[F, UserRepo, User]
135 | )(implicit F: FlatMap[F]): String => Kleisli[F, UserRepo, String] =
136 | (name: String) =>
137 | findUserMethod(name).flatMap { user =>
138 | getUserMethod(user.bossId).map { boss =>
139 | s"${user.name}'s boss is ${boss.name}"
140 | }
141 | }
142 |
143 |
144 | def userSummary(name: String): UserRepoReader[String] =
145 | userSummarizer[Id](getUser, findUser)(FlatMapId)(name)
146 |
147 | def userSummarySafe(name: String): UserRepoReaderOption[String] =
148 | userSummarizer[Option](getUserSafe, findUserSafe)(FlatMapOption)(name)
149 |
150 | }
151 |
152 |
153 |
154 | object UserExample extends App {
155 | import UserExperiment._
156 |
157 | val repo = new UserRepo {
158 | private val users = List(User(1, 1, "Vito"), User(2, 1, "Michael"), User(3,2, "Fredo"))
159 | def names: List[String] = users.map { user => user.name }
160 | // intentionally unsafe
161 | // will add failure context later
162 | def get(id: Long): User = users.find { user => user.id == id }.get
163 |
164 | def find(name: String): User = users.find { user => user.name == name }.get
165 | }
166 |
167 | println("describe Fredo")
168 | val describeFredo: String = userSummary("Fredo").run(repo)
169 |
170 | println(describeFredo)
171 |
172 | println("describe Sonny")
173 | val describeSonny: Option[String] =
174 | userSummarySafe("Sonny").run(repo)
175 |
176 | println(describeSonny)
177 |
178 |
179 | println("the absence of Sonny in the repository is handled safely")
180 | }
181 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture4/TraverseOption.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.tutorial.lecture4
2 |
3 | import cats.Traverse
4 | import cats.Applicative
5 | import cats.std.list._
6 |
7 | /*
8 | `traverse` and `sequence` from Chapter 4,
9 | implemented on Scala Collections' `Option`
10 |
11 | */
12 |
13 | object TraverseOption {
14 | /*
15 | `traverse2` and `sequence2` are implemented from scratch (`sequence2` uses `traverse2`)
16 | `traverse` and `sequence` are implemented on top of Cats.
17 | It is preferable to use the implementation of `traverse`
18 | provided by Cats.
19 |
20 | Cost of Cats implementation
21 | Cats' `traverse` implementation obfuscates the solution to our challenge
22 |
23 | Benefit of Cats implementation
24 | `traverse` as implemented in FPiS Chapter 4 is simplified.
25 | (A => Option[B]) => Option[List[B]] *really* should be a combinator on
26 | `List`, not `Option`.
27 |
28 | Cats and Scalaz both implement it this way.
29 |
30 | FPiS' implementation of `traverse` for both Option and Either is simplified
31 |
32 | */
33 |
34 | /*
35 | Implement `prepend`, a helper function for `traverse2`
36 | */
37 | private def prepend[A](opA: Option[A], opListA: Option[List[A]]): Option[List[A]] =
38 | opA.flatMap { (a: A) =>
39 | opListA.map { (listA: List[A]) => a :: listA }
40 | }
41 |
42 | /*
43 | Implement `traverse2` using `prepend`. Do not use `sequence2`.
44 | */
45 | def traverse2[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
46 | a.foldRight[Option[List[B]]](Some(List[B]())) { (a: A, opListB: Option[List[B]]) =>
47 | val opB: Option[B] = f(a)
48 | prepend(opB, opListB)
49 | }
50 |
51 | /*
52 | Implement `sequence2` using `traverse2`.
53 | The body of this method is a one-liner.
54 | */
55 | def sequence2[A, B](listOpA: List[Option[A]]): Option[List[A]] =
56 | traverse2(listOpA) { (opA: Option[A]) => opA }
57 |
58 | /*
59 | Requirement for production-ready implementation of `traverse` and `sequence` below.
60 | */
61 | implicit val applicativeOption = new Applicative[Option] {
62 | def pure[A](a: A): Option[A] = Some(a)
63 | // Members declared in cats.Apply
64 | def ap[A, B](ff: Option[A => B])(fa: Option[A]): Option[B] =
65 | (ff, fa) match {
66 | case (Some(ab), Some(a)) => Some(ab(a))
67 | case _ => None
68 | }
69 | // Members declared in cats.Cartesian
70 | override def product[A, B](fa: Option[A], fb: Option[B]): Option[(A, B)] =
71 | (fa, fb) match {
72 | case (Some(a), Some(b)) => Some((a, b))
73 | case _ => None
74 | }
75 | // Members declared in cats.Functor
76 | override def map[A, B](fa: Option[A])(f: A => B): Option[B] =
77 | fa.map(f)
78 | }
79 |
80 | /*
81 | Here, G = Option, and Applicative[G] = Applicative[Option]
82 | */
83 | def traverse[A, B](listA: List[A])(f: A => Option[B]): Option[List[B]] =
84 | Traverse[List].traverse(listA)(f)(applicativeOption)
85 |
86 | def sequence[A](loa: List[Option[A]]): Option[List[A]] =
87 | Traverse[List].traverse(loa) { (oa: Option[A]) => oa }(applicativeOption)
88 |
89 | }
90 |
91 | object TraverseExample extends App {
92 | import TraverseOption._
93 |
94 | val capitalLetterCodes: List[Int] = List(65, 66, 88, 89, 90)
95 |
96 | println("ASCII codes of a few capital letters")
97 | println(capitalLetterCodes)
98 |
99 | def capitalLetter(i: Int): Option[Char] =
100 | if (i >= 65 && i <= 90) Some(i.toChar)
101 | else None
102 |
103 | println("Use `traverse` to apply `capitalLetter(i: Int): Option[Char]` and receive Option[List[Char]]")
104 |
105 | val allCap: Option[List[Char]] =
106 | traverse(capitalLetterCodes)(capitalLetter)
107 |
108 | println(allCap)
109 |
110 | println("-------------------------")
111 |
112 | println("ASCII codes of some upper case letters and other ASCII characters")
113 | val mixedLetterCodes: List[Int] = List(60, 61, 62, 65, 66, 88, 89, 90, 98)
114 | println(mixedLetterCodes)
115 |
116 | println("Use `traverse` to apply `capitalLetter(i: Int): Option[Char]` and receive Option[List[Char]]")
117 |
118 | val severalCap: Option[List[Char]] =
119 | traverse(mixedLetterCodes)(capitalLetter)
120 |
121 | println(severalCap)
122 |
123 | }
124 |
125 | object SequenceExample extends App {
126 | import TraverseOption._
127 |
128 | val capitalLetterCodes: List[Int] = List(65, 66, 88, 89, 90)
129 |
130 | println("ASCII codes of a few capital letters")
131 | println(capitalLetterCodes)
132 |
133 | def capitalLetter(i: Int): Option[Char] =
134 | if (i >= 65 && i <= 90) Some(i.toChar)
135 | else None
136 |
137 | println("mapped to capital letters:")
138 |
139 | val capitalLetters: List[Option[Char]] =
140 | capitalLetterCodes.map { (i: Int) => capitalLetter(i) }
141 | println(capitalLetters)
142 |
143 | println("Use `sequence` to invert the containers List[Option[Char]] => Option[List[Char]]: ")
144 | val optionListCapitalLetters: Option[List[Char]] =
145 | sequence(capitalLetters)
146 | println(optionListCapitalLetters)
147 |
148 | println("-------------------------")
149 |
150 | println("ASCII codes of some upper case letters and other ASCII characters")
151 | val mixedLetterCodes: List[Int] = List(60, 61, 62, 65, 66, 88, 89, 90, 98)
152 | println(mixedLetterCodes)
153 |
154 | println("Only the ASCII codes for upper case letters are converted to characters: ")
155 | val mixedLetters: List[Option[Char]] =
156 | mixedLetterCodes.map { (i: Int) => capitalLetter(i) }
157 | println(mixedLetters)
158 |
159 | println("Use sequence to invert the containers List[Option[Char]] => Option[List[Char]]: ")
160 | val notAllCapital: Option[List[Char]] =
161 | sequence(mixedLetters)
162 | println(notAllCapital)
163 |
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/slides/lecture7.md:
--------------------------------------------------------------------------------
1 |
2 | #Lecture 7: Purely Functional Parallelism
3 |
4 | ---
5 |
6 | By now, we have implemented `unit`, `flatMap`, `map`, `map2` and other combinators on each of these:
7 |
8 | !scala
9 | trait List[+A] // chapter 3
10 |
11 | trait Option[+A] // chapter 4
12 |
13 | trait Stream[+A] // chapter 5
14 |
15 | type Rand[A] = RNG => (A, RNG) // chapter 6
16 |
17 |
18 | Implementing combinators on type `Rand` rather than a trait was a significant leap!
19 |
20 | ---
21 |
22 | Chapter 6 demonstrates *both* styles (trait and type), and why the latter style is necessary.
23 |
24 |
25 |
26 |
27 | We will develop several versions of a parallelism library, and demonstrate why each version is an improvement on the one prior.
28 |
29 |
30 |
31 |
32 | Chapter 6 demonstrates not only how to *use* a functional parallelism library, but how to to design one.
33 |
34 |
35 | ---
36 |
37 | # An early version of `Par`
38 |
39 | A trait for "containing" a parallel computation which produces `A`:
40 |
41 | !scala
42 | trait Par[+A] {
43 | ???
44 | }
45 |
46 | object Par {
47 | // entering the container
48 | def unit[A](a: => A): Par[A] = ???
49 | // exiting the container
50 | def get[A](parA: Par[A]): A = ???
51 | }
52 |
53 | ---
54 |
55 | !scala
56 | // listing 7.2
57 | def sum(ints: IndexedSeq[Int]): Int =
58 | if (ints.size <= 1)
59 | ints headOption getOrElse 0
60 | else {
61 | val (l: IndexedSeq[Int], r: IndexedSeq[Int]) =
62 | ints.splitAt(ints.length/2)
63 | val sumL: Par[Int] = Par.unit(sum(l))
64 | val sumR: Par[Int] = Par.unit(sum(r))
65 | val leftPlusRight: Int =
66 | Par.get(sumL) + Par.get(sumR)
67 | leftPlusRight
68 | }
69 |
70 | ---
71 |
72 | We need to make design decisions about `unit` and `get`
73 |
74 | !scala
75 | // entering the container
76 | def unit[A](a: => A): Par[A] = ???
77 | // exiting the container
78 | def get[A](parA: Par[A]): A = ???
79 |
80 |
81 | We will return to `sum` momentarily.
82 |
83 | ---
84 |
85 | # Non-blocking
86 |
87 | A non-blocking function call *does not stop* the progression of the program from top to bottom.
88 |
89 | Even if `expensiveNonblocking` does a lot of work, these three calls are made in quick succession because there is no *wait* for `expensiveNonblocking` to return.
90 |
91 | !scala
92 | def expensiveNonblocking(f: Foo): Unit = ???
93 |
94 | expensiveNonblocking(foo1)
95 | expensiveNonblocking(foo2)
96 | expensiveNonblocking(foo3)
97 |
98 | Registering a call-back is a great example of a non-blocking function call.
99 |
100 |
101 |
102 | ---
103 |
104 | # Separation of description from evaluation
105 |
106 |
107 |
108 |
109 | In the context of our parallelism library, *description* means:
110 |
111 | * when the computation is performed
112 | * on which thread the computation is performed
113 |
114 | Some of this description will be "locked in" by our parallelism library.
115 |
116 | Other parts of this description will be decided by the user of our parallelism library.
117 |
118 | We will strike a balance.
119 |
120 | ---
121 | # Design decisions about `get` and `unit`
122 |
123 | !scala
124 | val sumL: Par[Int] = Par.unit(sum(l))
125 | val sumR: Par[Int] = Par.unit(sum(r))
126 | val leftPlusRight: Int =
127 | Par.get(sumL) + Par.get(sumR)
128 |
129 | If `unit` is non-blocking and begins work:
130 |
131 | * `sumL` and `sumR` will be called in quick succession -- good
132 | * Some of the work of the left job and the right job will *already* be completed when we arrive at `leftPlusRight`
133 |
134 | ---
135 | # Design decisions about `get` and `unit`
136 |
137 | !scala
138 | val sumL: Par[Int] = Par.unit(sum(l))
139 | val sumR: Par[Int] = Par.unit(sum(r))
140 | val leftPlusRight: Int =
141 | Par.get(sumL) + Par.get(sumR)
142 |
143 | If `get` is blocking and begins work:
144 |
145 | * `Par.get(sumL)` will begin and finish its work *before* `Par.get(sumR)` is called -- no parallelism
146 |
147 | ---
148 |
149 | `sum` exposes the first weakness of our primitive parallelism library --
150 |
151 | *`get` has to block.*
152 |
153 | There is no other way than blocking to return a hard value `A`.
154 |
155 | def get[A](parA: Par[A]): A = ???
156 |
157 | ---
158 | # `get`, `unit` and `Par` must change
159 |
160 | We did not waste any time on the internal implementations of `get` and `unit` before deciding their external signatures must change. This is good design practice.
161 |
162 |
163 |
164 |
165 | Our new version of `get` is *non-blocking*, by virtue of a Java `Future`.
166 |
167 | We can retrieve a `Future[A]` without blocking.
168 |
169 | !scala
170 | def get[A](parA: Par[A]): Future[A] = ???
171 |
172 | ---
173 |
174 | # `get`, `unit` and `Par` must change
175 |
176 | Other parts must change to suite Java `Future`.
177 |
178 | We need an `ExecutorService` to produce a `Future`.
179 |
180 | !scala
181 | type Par[A] = ExecutorService => Future[A]
182 |
183 |
184 |
185 |
186 | It is analogous to needing an `RNG` to produce a random value.
187 |
188 | !scala
189 | type Rand[A] = RNG => (A, RNG)
190 |
191 | ---
192 |
193 | !scala
194 | type Par[A] = ExecutorService => Future[A]
195 |
196 | object Par {
197 | // entering the container
198 | def unit[A](a: => A): Par[A] = ???
199 | // exiting the container
200 | def get[A](parA: Par[A]): Future[A] = ???
201 | }
202 |
203 | ---
204 |
205 | # `fork`
206 |
207 | Earlier we said we would "strike a balance" between library control of parallelism and library user control of parallelism.
208 |
209 | `fork` is a powerful method for the library's user.
210 |
211 | `fork` places a job `parA` on another thread.
212 |
213 | !scala
214 | type Par[A] = ExecutorService => Future[A]
215 |
216 | def fork[A](parA: => Par[A]): Par[A] =
217 | executorService =>
218 | executorService.submit(new Callable[A] {
219 | def call = parA(executorService).get
220 | })
221 |
222 | Note that `fork` matches up with type `Par`.
223 |
224 | ---
225 |
226 | # Implementing more of the usual combinators
227 |
228 | !scala
229 | type Par[A] = ExecutorService => Future[A]
230 |
231 | object Par {
232 | ...
233 | def map2[A,B,C](parA: Par[A], parB: Par[B])
234 | (f: (A,B) => C): Par[C] =
235 |
236 |
237 |
238 | ---
239 |
240 | # scratch
241 |
242 | In Lectures X, 4, 4a, and 5, we implemented combinators on traits:
243 | In Lecture 6: "Random" Number Generators, we implemented combinators on a type:
244 |
245 | !scala
246 |
247 | ---
248 |
249 | #Homework
250 |
251 | Read Chapter 8 of _Functional Programming in Scala_.
252 |
--------------------------------------------------------------------------------
/slides/theme/base.html:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
26 |
27 |
28 | {{ head_title }}
29 |
30 | {% if embed %}
31 |
34 |
37 | {% else %}
38 |
39 |
40 | {% endif %}
41 | {% for css in user_css %}
42 | {% if embed %}
43 |
46 | {% else %}
47 |
48 | {% endif %}
49 | {% endfor %}
50 |
51 |
52 | {% if embed %}
53 |
56 | {% else %}
57 |
58 | {% endif %}
59 | {% for js in user_js %}
60 | {% if embed %}
61 |
64 | {% else %}
65 |
66 | {% endif %}
67 | {% endfor %}
68 |
69 |
81 |
84 |
85 |
86 |
87 |
88 |
91 |
92 | {% for slide in slides %}
93 |
94 |
95 |
96 |
97 | {% if slide.header %}
98 |
99 | {% endif %}
100 | {% if slide.content %}
101 |
102 | {% endif %}
103 |
104 |
105 |
106 |
107 | {% if slide.presenter_notes %}
108 | {{ slide.presenter_notes }}
109 | {% endif %}
110 |
111 |
112 |
113 |
114 |
115 | {% if slide.source %}
116 |
119 | {% endif %}
120 |
121 | {{ slide.number }}/{{ num_slides }}
122 |
123 |
124 |
125 |
126 |
127 | {% endfor %}
128 |
129 |
130 | {% if toc %}
131 |
151 | {% endif %}
152 |
198 |
199 |
200 |
201 |
--------------------------------------------------------------------------------
/tutorial/src/main/scala/com/datascience/education/tutorial/lecture2/Folding.scala:
--------------------------------------------------------------------------------
1 |
2 | package com.datascience.education.tutorial.lecture2
3 |
4 | import scala.language.postfixOps
5 |
6 | object Folding {
7 |
8 | // PART 1
9 | // 1a
10 | def myToString[A](list: List[A]): String = list match {
11 | case Nil => "Foo"
12 | case head :: tail => "Cons(" + head + ", " + myToString(tail) + ")"
13 | }
14 | myToString(List(1,2,3,4,5))
15 |
16 | // TASK 1b
17 | def foo(head: String, tail: String): String =
18 | "Cons(" + head + ", " + tail + ")"
19 |
20 | def myToString2(list: List[String]) = foldRight(list,"Nil")(foo)
21 |
22 | myToString2(List("1","2","3","4","5"))
23 |
24 | // Part 1c
25 | def foo2[A](head: A, tail: String): String =
26 | "Cons(" + head + ", " + tail + ")"
27 |
28 | def myToString3[A](list: List[A]) =
29 | foldRight[A,String](list,"Nil")(foo2)
30 |
31 | myToString3(List(1,2,3,4,5))
32 |
33 | // verbose
34 | foldRightPrinter(List(1,2,3,4,5),"Nil")(foo2)
35 |
36 | // PART 2
37 | // part 2a
38 | def foldRight[A, B](list: List[A], z: B)(f:(A, B) => B): B =
39 | list match {
40 | case Nil => z
41 | case x :: xs => f(x, foldRight(xs, z)(f))
42 | }
43 |
44 | def foldRightPrinter[A, B](list: List[A], z: B)(f:(A, B) => B): B = {
45 | println(s"input: $list")
46 | val out: B = list match {
47 | case Nil => z
48 | case x :: xs => f(x, foldRightPrinter(xs, z)(f))
49 | }
50 | println(s"output: $out")
51 | out
52 | }
53 |
54 |
55 | // part 2b
56 | @annotation.tailrec
57 | def foldLeft[A,B](list: List[A], z: B)(f: (B, A) => B): B =
58 | list match {
59 | case Nil => z
60 | case head :: tail => foldLeft(tail,f(z,head) )(f)
61 | }
62 |
63 | def foldLeftPrinter[A,B](list: List[A], z: B)(f: (B, A) => B): B = {
64 | println(s"input: $list")
65 | val out: B = list match {
66 | case Nil => z
67 | case head :: tail => {
68 | val zi: B = f(z, head)
69 | println(s"f($z, $head) = $zi")
70 | foldLeftPrinter(tail, zi)(f)
71 | }
72 | }
73 | println(s"output: $out")
74 | out
75 | }
76 |
77 | def foo4[A](tail: String, head: A): String =
78 | "Cons(" + head + ", " + tail + ")"
79 |
80 | def myToString4[A](list: List[A]): String = foldLeft(list,"Nil")(foo4)
81 |
82 | myToString4(List(1,2,3,4))
83 |
84 | foldLeftPrinter(List(1,2,3,4),"Nil")(foo4)
85 |
86 |
87 | @annotation.tailrec
88 | def tailRecursive(i: Int): Int =
89 | if (i>100) i
90 | else tailRecursive(i+1)
91 |
92 | def notTailRecursive(i: Int): Int =
93 | if (i > 100) i
94 | else i + notTailRecursive(i+1)
95 |
96 |
97 | // part 2c
98 |
99 | def sumFoldRight(list: List[Int]): Int =
100 | foldRight(list, 0)((next: Int, sum: Int) => next+sum)
101 |
102 | def sumFoldLeft(list: List[Int]): Int =
103 | foldLeft(list, 0)((sum: Int, next: Int) => next+sum)
104 |
105 |
106 | def foldRightViaFoldLeft[A,B](l: List[A], z: B)(f: (A,B) => B): B =
107 | foldLeft(l.reverse, z)((b,a) => f(a,b))
108 |
109 | def myToString5(list: List[String]) =
110 | foldRightViaFoldLeft(list,"Nil")(foo)
111 |
112 | myToString5(List("1","2","3","4","5"))
113 |
114 | //difference between Reduce and foldLeft
115 | //http://stackoverflow.com/questions/25158780/difference-between-reduce-and-foldleft-fold-in-functional-programming-particula
116 |
117 |
118 | //List also has foldRight and foldLeft as methods
119 |
120 | val l = (0 to 5).toList
121 | //l.foldLeft(0)(_+_)
122 | //l.foldLeft(0)((r,c) => r + c)
123 | //l.foldLeft(0)((s,_) => s + 1)
124 | l.foldLeft(List[Int]())((r,c) => c :: r)
125 |
126 | // PART 3
127 | // 3a
128 | def average(list: List[Double]): Double =
129 | list.foldLeft(0.0)(_+_) /
130 | list.foldLeft(0.0)((count, next) => count+1)
131 |
132 | // TASK
133 |
134 | def average2(list: List[Double]): Double = {
135 |
136 | val tuple: (Double, Int) =
137 | list.foldLeft[(Double,Int)]((0.0, 0)){
138 | case ((sum: Double, count: Int), next: Double) =>
139 | ???
140 | }
141 |
142 | tuple._1 / tuple._2
143 | }
144 |
145 | val r = scala.util.Random
146 | r.nextDouble //returns a value between 0.0 and 1.0
147 | val l2 = average((for {i <- (0 to 400)} yield r.nextDouble).toList)
148 |
149 |
150 | // TASK 3b
151 |
152 | def contains[A](list: List[A], item: A): Boolean =
153 | list.foldLeft(???)(???)
154 |
155 |
156 | // contains(List(1,2,3,4),4)
157 |
158 |
159 | // TASK 3c
160 | def last[A](list: List[A]): A = list.foldLeft[A](???)(???)
161 |
162 |
163 |
164 | // last(List(1,2,3,4))
165 |
166 |
167 |
168 | // TASK 3d
169 | def penultimate[A](list: List[A]): A = ???
170 | // list.foldLeft( (???, ???) )((???, ???) => ??? )//???
171 |
172 | // List(1,2,3,4).tail.head // hint- use (list.head, list.tail.head) as the initial value
173 | // val a = (2,3)
174 | // a._1
175 | // penultimate(List(1,2,3,4))
176 |
177 |
178 | // TASK 3e
179 | def average3(list: List[Double]): Double = ???
180 | // list match {
181 | // ???
182 | // }
183 |
184 | // average3(List(1,2,3,4))
185 |
186 |
187 | // TASK 3f
188 | def kthLast[A](l: List[A], k: Int) = {
189 | ???
190 | l.foldRight(???)(???)//.???
191 | }
192 |
193 | // kthLast(l,2)
194 |
195 | // TASK 3g
196 | def passThrough[A](list: List[A]): List[A] =
197 | ???
198 |
199 | // TASK 3h
200 | //curly braces are ok for function literals
201 | def mapViaFoldLeft[A,B](list: List[A], f: A => B): List[B] =
202 | ???
203 |
204 | val l3 = (0 to 5).toList
205 |
206 | // mapViaFoldLeft(l3,(a: Int) => a*2)
207 |
208 | // TASK 3i
209 | def unique[A](list: List[A]): List[A] =
210 | ???
211 |
212 | // unique(List(2,3,2,3,2,3,1,4))
213 | List(1,2,3).contains(2) //hint- use contains
214 |
215 | // TASK 3j
216 | def double[A](list: List[A]): List[A] =
217 | ???
218 |
219 |
220 | // TASK 3k
221 |
222 | val l4 = List(1,2,3) ::: List(4,5,6)
223 | l4.partition(_ > 2)
224 |
225 | def stackSort[A : Ordering](list: List[A]): List[A] =
226 | ???
227 |
228 |
229 | val l5 = List(9,3,4,2,6,4,8,1,5,63)
230 | // println(l5.mkString(","))
231 | // println(stackSort(l5).mkString(","))
232 |
233 |
234 |
235 | // TASK 3l
236 |
237 | def updateDiffs(tup: (Int, Int, Int), x: Int): (Int, Int, Int) =
238 | tup match {
239 | case (mn, mx, diff) if ??? => ??? // a new low
240 | case (mn, mx, diff) if ??? => ??? // a new high
241 | case _ => ??? // element x was not higher or lower than elements previously encountered. Maximum difference remains the same.
242 | }
243 |
244 |
245 | def maxDifference(list: List[Int]): Int = list match {
246 | case Nil => -1
247 | case head :: tail => ???
248 | }
249 |
250 | // maxDifference(List(2,3,10,2,4,8,1))
251 | // maxDifference(List(7,9,5,6,3,2))
252 |
253 | }
254 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/sbtTutorial/sbt_command_and_tasks.txt:
--------------------------------------------------------------------------------
1 |
2 | Most important commands -- common to all SBT projects
3 |
4 | > help
5 | help
6 |
7 | help Displays this help message or prints detailed help on requested commands (run 'help ').
8 | completions Displays a list of completions for the given argument string (run 'completions ').
9 | about Displays basic information about sbt and the build.
10 | tasks Lists the tasks defined for the current project.
11 | reload (Re)loads the current project or changes to plugins project or returns from it.
12 | projects Lists the names of available projects or temporarily adds/removes extra builds to the session.
13 | project Displays the current project or changes to the provided `project`.
14 | last Displays output from a previous command or the output from a specific task.
15 | last-grep Shows lines from the last output for 'key' that match 'pattern'.
16 | exit Terminates the build.
17 |
18 | All commands -- common to all SBT projects
19 |
20 | > help
21 | help
22 |
23 | debugging Add debugging flags to all forked JVM processes.
24 | debugging-off Remove debugging flags from all forked JVM processes.
25 | help Displays this help message or prints detailed help on requested commands (run 'help ').
26 | completions Displays a list of completions for the given argument string (run 'completions ').
27 | about Displays basic information about sbt and the build.
28 | tasks Lists the tasks defined for the current project.
29 | settings Lists the settings defined for the current project.
30 | reload (Re)loads the current project or changes to plugins project or returns from it.
31 | projects Lists the names of available projects or temporarily adds/removes extra builds to the session.
32 | project Displays the current project or changes to the provided `project`.
33 | set [every] Evaluates a Setting and applies it to the current project.
34 | session Manipulates session settings. For details, run 'help session'.
35 | inspect [uses|tree|definitions] Prints the value for 'key', the defining scope, delegates, related definitions, and dependencies.
36 | Sets the logging level to 'log-level'. Valid levels: debug, info, warn, error
37 | plugins Lists currently available plugins.
38 | ; (; )* Runs the provided semicolon-separated commands.
39 | ~ Executes the specified command whenever source files change.
40 | last Displays output from a previous command or the output from a specific task.
41 | last-grep Shows lines from the last output for 'key' that match 'pattern'.
42 | export + Executes tasks and displays the equivalent command lines.
43 | exit Terminates the build.
44 | -- Schedules a command to run before other commands on startup.
45 | show Displays the result of evaluating the setting or task associated with 'key'.
46 | all + Executes all of the specified tasks concurrently.
47 |
48 |
49 | Most important tasks -- configurable by SBT project but usually unchanged
50 |
51 | This is a list of tasks defined for the current project.
52 | It does not list the scopes the tasks are defined in; use the 'inspect' command for that.
53 | Tasks produce values. Use the 'show' command to run the task and print the resulting value.
54 |
55 | clean Deletes files produced by the build, such as generated sources, compiled classes, and task caches.
56 | compile Compiles sources.
57 | console Starts the Scala interpreter with the project classes on the classpath.
58 | consoleProject Starts the Scala interpreter with the sbt and the build definition on the classpath and useful imports.
59 | consoleQuick Starts the Scala interpreter with the project dependencies on the classpath.
60 | run Runs a main class, passing along arguments provided on the command line.
61 | runMain Runs the main class selected by the first argument, passing the remaining arguments to the main method.
62 | test Executes all tests.
63 |
64 |
65 |
66 | All tasks -- configurable by SBT project but usually unchanged
67 |
68 | This is a list of tasks defined for the current project.
69 | It does not list the scopes the tasks are defined in; use the 'inspect' command for that.
70 | Tasks produce values. Use the 'show' command to run the task and print the resulting value.
71 |
72 | clean Deletes files produced by the build, such as generated sources, compiled classes, and task caches.
73 | compile Compiles sources.
74 | console Starts the Scala interpreter with the project classes on the classpath.
75 | consoleProject Starts the Scala interpreter with the sbt and the build definition on the classpath and useful imports.
76 | consoleQuick Starts the Scala interpreter with the project dependencies on the classpath.
77 | copyResources Copies resources to the output directory.
78 | doc Generates API documentation.
79 | package Produces the main artifact, such as a binary jar. This is typically an alias for the task that actually does the packaging.
80 | packageBin Produces a main artifact, such as a binary jar.
81 | packageDoc Produces a documentation artifact, such as a jar containing API documentation.
82 | packageSrc Produces a source artifact, such as a jar containing sources and resources.
83 | publish Publishes artifacts to a repository.
84 | publishLocal Publishes artifacts to the local Ivy repository.
85 | publishM2 Publishes artifacts to the local Maven repository.
86 | run Runs a main class, passing along arguments provided on the command line.
87 | runMain Runs the main class selected by the first argument, passing the remaining arguments to the main method.
88 | test Executes all tests.
89 | testOnly Executes the tests provided as arguments or all tests if no arguments are provided.
90 | testQuick Executes the tests that either failed before, were not run or whose transitive dependencies changed, among those provided as arguments.
91 | update Resolves and optionally retrieves dependencies, producing a report.
92 |
--------------------------------------------------------------------------------
/stylesheets/styles.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Noto Sans';
3 | font-weight: 400;
4 | font-style: normal;
5 | src: url('../fonts/Noto-Sans-regular/Noto-Sans-regular.eot');
6 | src: url('../fonts/Noto-Sans-regular/Noto-Sans-regular.eot?#iefix') format('embedded-opentype'),
7 | local('Noto Sans'),
8 | local('Noto-Sans-regular'),
9 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.woff2') format('woff2'),
10 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.woff') format('woff'),
11 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.ttf') format('truetype'),
12 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.svg#NotoSans') format('svg');
13 | }
14 |
15 | @font-face {
16 | font-family: 'Noto Sans';
17 | font-weight: 700;
18 | font-style: normal;
19 | src: url('../fonts/Noto-Sans-700/Noto-Sans-700.eot');
20 | src: url('../fonts/Noto-Sans-700/Noto-Sans-700.eot?#iefix') format('embedded-opentype'),
21 | local('Noto Sans Bold'),
22 | local('Noto-Sans-700'),
23 | url('../fonts/Noto-Sans-700/Noto-Sans-700.woff2') format('woff2'),
24 | url('../fonts/Noto-Sans-700/Noto-Sans-700.woff') format('woff'),
25 | url('../fonts/Noto-Sans-700/Noto-Sans-700.ttf') format('truetype'),
26 | url('../fonts/Noto-Sans-700/Noto-Sans-700.svg#NotoSans') format('svg');
27 | }
28 |
29 | @font-face {
30 | font-family: 'Noto Sans';
31 | font-weight: 400;
32 | font-style: italic;
33 | src: url('../fonts/Noto-Sans-italic/Noto-Sans-italic.eot');
34 | src: url('../fonts/Noto-Sans-italic/Noto-Sans-italic.eot?#iefix') format('embedded-opentype'),
35 | local('Noto Sans Italic'),
36 | local('Noto-Sans-italic'),
37 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.woff2') format('woff2'),
38 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.woff') format('woff'),
39 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.ttf') format('truetype'),
40 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.svg#NotoSans') format('svg');
41 | }
42 |
43 | @font-face {
44 | font-family: 'Noto Sans';
45 | font-weight: 700;
46 | font-style: italic;
47 | src: url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot');
48 | src: url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot?#iefix') format('embedded-opentype'),
49 | local('Noto Sans Bold Italic'),
50 | local('Noto-Sans-700italic'),
51 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff2') format('woff2'),
52 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff') format('woff'),
53 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.ttf') format('truetype'),
54 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.svg#NotoSans') format('svg');
55 | }
56 |
57 | body {
58 | background-color: #fff;
59 | padding:50px;
60 | font: 14px/1.5 "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
61 | color:#727272;
62 | font-weight:400;
63 | }
64 |
65 | h1, h2, h3, h4, h5, h6 {
66 | color:#222;
67 | margin:0 0 20px;
68 | }
69 |
70 | p, ul, ol, table, pre, dl {
71 | margin:0 0 20px;
72 | }
73 |
74 | h1, h2, h3 {
75 | line-height:1.1;
76 | }
77 |
78 | h1 {
79 | font-size:28px;
80 | }
81 |
82 | h2 {
83 | color:#393939;
84 | }
85 |
86 | h3, h4, h5, h6 {
87 | color:#494949;
88 | }
89 |
90 | a {
91 | color:#39c;
92 | text-decoration:none;
93 | }
94 |
95 | a:hover {
96 | color:#069;
97 | }
98 |
99 | a small {
100 | font-size:11px;
101 | color:#777;
102 | margin-top:-0.3em;
103 | display:block;
104 | }
105 |
106 | a:hover small {
107 | color:#777;
108 | }
109 |
110 | .wrapper {
111 | width:860px;
112 | margin:0 auto;
113 | }
114 |
115 | blockquote {
116 | border-left:1px solid #e5e5e5;
117 | margin:0;
118 | padding:0 0 0 20px;
119 | font-style:italic;
120 | }
121 |
122 | code, pre {
123 | font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal, Consolas, Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
124 | color:#333;
125 | font-size:12px;
126 | }
127 |
128 | pre {
129 | padding:8px 15px;
130 | background: #f8f8f8;
131 | border-radius:5px;
132 | border:1px solid #e5e5e5;
133 | overflow-x: auto;
134 | }
135 |
136 | table {
137 | width:100%;
138 | border-collapse:collapse;
139 | }
140 |
141 | th, td {
142 | text-align:left;
143 | padding:5px 10px;
144 | border-bottom:1px solid #e5e5e5;
145 | }
146 |
147 | dt {
148 | color:#444;
149 | font-weight:700;
150 | }
151 |
152 | th {
153 | color:#444;
154 | }
155 |
156 | img {
157 | max-width:100%;
158 | }
159 |
160 | header {
161 | width:270px;
162 | float:left;
163 | position:fixed;
164 | -webkit-font-smoothing:subpixel-antialiased;
165 | }
166 |
167 | header ul {
168 | list-style:none;
169 | height:40px;
170 | padding:0;
171 | background: #f4f4f4;
172 | border-radius:5px;
173 | border:1px solid #e0e0e0;
174 | width:270px;
175 | }
176 |
177 | header li {
178 | width:89px;
179 | float:left;
180 | border-right:1px solid #e0e0e0;
181 | height:40px;
182 | }
183 |
184 | header li:first-child a {
185 | border-radius:5px 0 0 5px;
186 | }
187 |
188 | header li:last-child a {
189 | border-radius:0 5px 5px 0;
190 | }
191 |
192 | header ul a {
193 | line-height:1;
194 | font-size:11px;
195 | color:#999;
196 | display:block;
197 | text-align:center;
198 | padding-top:6px;
199 | height:34px;
200 | }
201 |
202 | header ul a:hover {
203 | color:#999;
204 | }
205 |
206 | header ul a:active {
207 | background-color:#f0f0f0;
208 | }
209 |
210 | strong {
211 | color:#222;
212 | font-weight:700;
213 | }
214 |
215 | header ul li + li + li {
216 | border-right:none;
217 | width:89px;
218 | }
219 |
220 | header ul a strong {
221 | font-size:14px;
222 | display:block;
223 | color:#222;
224 | }
225 |
226 | section {
227 | width:500px;
228 | float:right;
229 | padding-bottom:50px;
230 | }
231 |
232 | small {
233 | font-size:11px;
234 | }
235 |
236 | hr {
237 | border:0;
238 | background:#e5e5e5;
239 | height:1px;
240 | margin:0 0 20px;
241 | }
242 |
243 | footer {
244 | width:270px;
245 | float:left;
246 | position:fixed;
247 | bottom:50px;
248 | -webkit-font-smoothing:subpixel-antialiased;
249 | }
250 |
251 | @media print, screen and (max-width: 960px) {
252 |
253 | div.wrapper {
254 | width:auto;
255 | margin:0;
256 | }
257 |
258 | header, section, footer {
259 | float:none;
260 | position:static;
261 | width:auto;
262 | }
263 |
264 | header {
265 | padding-right:320px;
266 | }
267 |
268 | section {
269 | border:1px solid #e5e5e5;
270 | border-width:1px 0;
271 | padding:20px 0;
272 | margin:0 0 20px;
273 | }
274 |
275 | header a small {
276 | display:inline;
277 | }
278 |
279 | header ul {
280 | position:absolute;
281 | right:50px;
282 | top:52px;
283 | }
284 | }
285 |
286 | @media print, screen and (max-width: 720px) {
287 | body {
288 | word-wrap:break-word;
289 | }
290 |
291 | header {
292 | padding:0;
293 | }
294 |
295 | header ul, header p.view {
296 | position:static;
297 | }
298 |
299 | pre, code {
300 | word-wrap:normal;
301 | }
302 | }
303 |
304 | @media print, screen and (max-width: 480px) {
305 | body {
306 | padding:15px;
307 | }
308 |
309 | header ul {
310 | width:99%;
311 | }
312 |
313 | header li, header ul li + li + li {
314 | width:33%;
315 | }
316 | }
317 |
318 | @media print {
319 | body {
320 | padding:0.4in;
321 | font-size:12pt;
322 | color:#444;
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/docs/stylesheets/styles.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Noto Sans';
3 | font-weight: 400;
4 | font-style: normal;
5 | src: url('../fonts/Noto-Sans-regular/Noto-Sans-regular.eot');
6 | src: url('../fonts/Noto-Sans-regular/Noto-Sans-regular.eot?#iefix') format('embedded-opentype'),
7 | local('Noto Sans'),
8 | local('Noto-Sans-regular'),
9 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.woff2') format('woff2'),
10 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.woff') format('woff'),
11 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.ttf') format('truetype'),
12 | url('../fonts/Noto-Sans-regular/Noto-Sans-regular.svg#NotoSans') format('svg');
13 | }
14 |
15 | @font-face {
16 | font-family: 'Noto Sans';
17 | font-weight: 700;
18 | font-style: normal;
19 | src: url('../fonts/Noto-Sans-700/Noto-Sans-700.eot');
20 | src: url('../fonts/Noto-Sans-700/Noto-Sans-700.eot?#iefix') format('embedded-opentype'),
21 | local('Noto Sans Bold'),
22 | local('Noto-Sans-700'),
23 | url('../fonts/Noto-Sans-700/Noto-Sans-700.woff2') format('woff2'),
24 | url('../fonts/Noto-Sans-700/Noto-Sans-700.woff') format('woff'),
25 | url('../fonts/Noto-Sans-700/Noto-Sans-700.ttf') format('truetype'),
26 | url('../fonts/Noto-Sans-700/Noto-Sans-700.svg#NotoSans') format('svg');
27 | }
28 |
29 | @font-face {
30 | font-family: 'Noto Sans';
31 | font-weight: 400;
32 | font-style: italic;
33 | src: url('../fonts/Noto-Sans-italic/Noto-Sans-italic.eot');
34 | src: url('../fonts/Noto-Sans-italic/Noto-Sans-italic.eot?#iefix') format('embedded-opentype'),
35 | local('Noto Sans Italic'),
36 | local('Noto-Sans-italic'),
37 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.woff2') format('woff2'),
38 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.woff') format('woff'),
39 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.ttf') format('truetype'),
40 | url('../fonts/Noto-Sans-italic/Noto-Sans-italic.svg#NotoSans') format('svg');
41 | }
42 |
43 | @font-face {
44 | font-family: 'Noto Sans';
45 | font-weight: 700;
46 | font-style: italic;
47 | src: url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot');
48 | src: url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.eot?#iefix') format('embedded-opentype'),
49 | local('Noto Sans Bold Italic'),
50 | local('Noto-Sans-700italic'),
51 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff2') format('woff2'),
52 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.woff') format('woff'),
53 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.ttf') format('truetype'),
54 | url('../fonts/Noto-Sans-700italic/Noto-Sans-700italic.svg#NotoSans') format('svg');
55 | }
56 |
57 | body {
58 | background-color: #fff;
59 | padding:50px;
60 | font: 14px/1.5 "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
61 | color:#727272;
62 | font-weight:400;
63 | }
64 |
65 | h1, h2, h3, h4, h5, h6 {
66 | color:#222;
67 | margin:0 0 20px;
68 | }
69 |
70 | p, ul, ol, table, pre, dl {
71 | margin:0 0 20px;
72 | }
73 |
74 | h1, h2, h3 {
75 | line-height:1.1;
76 | }
77 |
78 | h1 {
79 | font-size:28px;
80 | }
81 |
82 | h2 {
83 | color:#393939;
84 | }
85 |
86 | h3, h4, h5, h6 {
87 | color:#494949;
88 | }
89 |
90 | a {
91 | color:#39c;
92 | text-decoration:none;
93 | }
94 |
95 | a:hover {
96 | color:#069;
97 | }
98 |
99 | a small {
100 | font-size:11px;
101 | color:#777;
102 | margin-top:-0.3em;
103 | display:block;
104 | }
105 |
106 | a:hover small {
107 | color:#777;
108 | }
109 |
110 | .wrapper {
111 | width:860px;
112 | margin:0 auto;
113 | }
114 |
115 | blockquote {
116 | border-left:1px solid #e5e5e5;
117 | margin:0;
118 | padding:0 0 0 20px;
119 | font-style:italic;
120 | }
121 |
122 | code, pre {
123 | font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal, Consolas, Liberation Mono, DejaVu Sans Mono, Courier New, monospace;
124 | color:#333;
125 | font-size:12px;
126 | }
127 |
128 | pre {
129 | padding:8px 15px;
130 | background: #f8f8f8;
131 | border-radius:5px;
132 | border:1px solid #e5e5e5;
133 | overflow-x: auto;
134 | }
135 |
136 | table {
137 | width:100%;
138 | border-collapse:collapse;
139 | }
140 |
141 | th, td {
142 | text-align:left;
143 | padding:5px 10px;
144 | border-bottom:1px solid #e5e5e5;
145 | }
146 |
147 | dt {
148 | color:#444;
149 | font-weight:700;
150 | }
151 |
152 | th {
153 | color:#444;
154 | }
155 |
156 | img {
157 | max-width:100%;
158 | }
159 |
160 | header {
161 | width:270px;
162 | float:left;
163 | position:fixed;
164 | -webkit-font-smoothing:subpixel-antialiased;
165 | }
166 |
167 | header ul {
168 | list-style:none;
169 | height:40px;
170 | padding:0;
171 | background: #f4f4f4;
172 | border-radius:5px;
173 | border:1px solid #e0e0e0;
174 | width:270px;
175 | }
176 |
177 | header li {
178 | width:89px;
179 | float:left;
180 | border-right:1px solid #e0e0e0;
181 | height:40px;
182 | }
183 |
184 | header li:first-child a {
185 | border-radius:5px 0 0 5px;
186 | }
187 |
188 | header li:last-child a {
189 | border-radius:0 5px 5px 0;
190 | }
191 |
192 | header ul a {
193 | line-height:1;
194 | font-size:11px;
195 | color:#999;
196 | display:block;
197 | text-align:center;
198 | padding-top:6px;
199 | height:34px;
200 | }
201 |
202 | header ul a:hover {
203 | color:#999;
204 | }
205 |
206 | header ul a:active {
207 | background-color:#f0f0f0;
208 | }
209 |
210 | strong {
211 | color:#222;
212 | font-weight:700;
213 | }
214 |
215 | header ul li + li + li {
216 | border-right:none;
217 | width:89px;
218 | }
219 |
220 | header ul a strong {
221 | font-size:14px;
222 | display:block;
223 | color:#222;
224 | }
225 |
226 | section {
227 | width:500px;
228 | float:right;
229 | padding-bottom:50px;
230 | }
231 |
232 | small {
233 | font-size:11px;
234 | }
235 |
236 | hr {
237 | border:0;
238 | background:#e5e5e5;
239 | height:1px;
240 | margin:0 0 20px;
241 | }
242 |
243 | footer {
244 | width:270px;
245 | float:left;
246 | position:fixed;
247 | bottom:50px;
248 | -webkit-font-smoothing:subpixel-antialiased;
249 | }
250 |
251 | @media print, screen and (max-width: 960px) {
252 |
253 | div.wrapper {
254 | width:auto;
255 | margin:0;
256 | }
257 |
258 | header, section, footer {
259 | float:none;
260 | position:static;
261 | width:auto;
262 | }
263 |
264 | header {
265 | padding-right:320px;
266 | }
267 |
268 | section {
269 | border:1px solid #e5e5e5;
270 | border-width:1px 0;
271 | padding:20px 0;
272 | margin:0 0 20px;
273 | }
274 |
275 | header a small {
276 | display:inline;
277 | }
278 |
279 | header ul {
280 | position:absolute;
281 | right:50px;
282 | top:52px;
283 | }
284 | }
285 |
286 | @media print, screen and (max-width: 720px) {
287 | body {
288 | word-wrap:break-word;
289 | }
290 |
291 | header {
292 | padding:0;
293 | }
294 |
295 | header ul, header p.view {
296 | position:static;
297 | }
298 |
299 | pre, code {
300 | word-wrap:normal;
301 | }
302 | }
303 |
304 | @media print, screen and (max-width: 480px) {
305 | body {
306 | padding:15px;
307 | }
308 |
309 | header ul {
310 | width:99%;
311 | }
312 |
313 | header li, header ul li + li + li {
314 | width:33%;
315 | }
316 | }
317 |
318 | @media print {
319 | body {
320 | padding:0.4in;
321 | font-size:12pt;
322 | color:#444;
323 | }
324 | }
325 |
--------------------------------------------------------------------------------
/misc/src/main/scala/com/datascience/education/misc/sudoku/Sudoku.scala:
--------------------------------------------------------------------------------
1 | package com.datascience.education.misc.sudoku
2 |
3 | import scala.language.postfixOps
4 |
5 | object Sudoku {
6 |
7 | /*
8 | "Unflatten" an element and return the row index it belongs to.
9 | 0-indexed
10 |
11 | val initial = List((0,5),(1,3),(4,7),(9,6),(12,1),(13,9),(14,5),...
12 |
13 | For example, element (1,3) belongs to row 0.
14 | */
15 | def row(element: (Int, Int)): Int = element._1 / 9
16 |
17 | /*
18 | "Unflatten" an element and return the column index it belongs to.
19 | 0-indexed
20 |
21 | For example, element(1,3) belongs to column 1.
22 | */
23 | def column(element: (Int, Int)): Int = element._1 % 9
24 |
25 | /*
26 | Tiles
27 |
28 | v index 0
29 | > 0 0 0 1 1 1 2 2 2
30 | 0 0 0 1 1 1 2 2 2
31 | 0 0 0 1 1 1 2 2 2
32 | 3 3 3 4 4 4 5 5 5
33 | 3 3 3 4 4 4 5 5 5
34 | 3 3 3 4 4 4 5 5 5
35 | 6 6 6 7 7 7 8 8 8 <- index 62
36 | 6 6 6 7 7 7 8 8 8
37 | 6 6 6 7 7 7 8 8 8 <- index 80
38 | ^
39 | "Unflatten" an element and return the tile index it belongs to.
40 |
41 | For example, element (1,3) belongs to tile 0.
42 |
43 | */
44 |
45 | def tile(element: (Int, Int)): Int = {
46 | val j = column(element)
47 | val tileX = j/3
48 |
49 | val i = row(element)
50 | val tileY = i/3
51 |
52 | val tile = tileX+3*tileY
53 |
54 | tile
55 | }
56 |
57 |
58 | /*
59 | 1a
60 | check rows for uniqueness
61 |
62 | In a valid Sudoku board,
63 | each row contains only one copy of each digit 1-9.
64 | In other words, the 9 elements of each row correspond one-to-one with the 9 digits in [1,9].
65 |
66 | Check that each row satisfies this requirement.
67 | */
68 | def checkX(board: List[(Int,Int)], next: (Int,Int)): Boolean = {
69 | def check(b: Boolean, tuple: (Int,Int)): Boolean = {
70 | b && ((row(tuple) == row(next) && tuple._2 != next._2) || row(tuple) != row(next))
71 |
72 | }
73 |
74 | val c = board.foldLeft(0)((count: Int, _: (Int, Int)) => count+1)
75 |
76 |
77 | board.foldLeft(true)(check)
78 | }
79 |
80 | /*
81 | TASK
82 | check columns for uniqueness
83 |
84 | In a valid Sudoku board,
85 | each column contains only one copy of each digit 1-9.
86 | This is the same rule as in `checkX`, but for columns.
87 |
88 | Check that each column satisfies this requirement.
89 | */
90 | def checkY(board: List[(Int,Int)], next: (Int,Int)): Boolean = ???
91 |
92 | /*
93 | TASK 1b
94 | check tiles for uniqueness
95 |
96 | A Sudoku board contains 9 3x3 tiles.
97 |
98 | As with each row and each column of the board,
99 | each tile must contain only one copy of each digit 1-9.
100 |
101 | */
102 | def checkT(board: List[(Int,Int)], next: (Int,Int)): Boolean = ???
103 | /*
104 | TASK 1c
105 | check that a given position has not been filled
106 | */
107 | def notPlayed(board: List[(Int,Int)], index: Int): Boolean = ???
108 |
109 | /*
110 | TASK 1d
111 | check that a given position is legal with respect to checkX, checkY, checkT
112 | */
113 | def isLegal(board: List[(Int,Int)], next: (Int,Int)): Boolean = ???
114 |
115 | //recursively provide all solutions to puzzle w/ given initial conds
116 | def sudokuSolve(initial: List[(Int,Int)]): Set[List[(Int,Int)]] = {
117 |
118 | // indices of empty board elements, given initial conditions (pre-filled board)
119 | val indices: List[Int] =
120 | (0 until 81) filter { index => notPlayed(initial, index)} toList
121 |
122 | /*
123 | TASK 1e
124 | */
125 | def sudokuIter(indices: List[Int]): Set[List[(Int,Int)]] = ???
126 |
127 | sudokuIter(indices)
128 | }
129 |
130 |
131 | def sudokuAll(index: Int): Set[List[(Int,Int)]] = {
132 | if (index == -1) Set(List())
133 | else
134 | for {
135 | board <- sudokuAll(index-1)
136 | k <- 0 until 9
137 | if isLegal(board, (index,k))
138 | } yield (index,k)::board
139 | }
140 |
141 | //plotting util
142 | def sudokuPlot(board: List[(Int,Int)]): String = {
143 | val out = Array.ofDim[Int](9,9)
144 | for {move <- board} out(move._1 / 9)(move._1 % 9) = move._2
145 | out.map({_.mkString(" ")}).mkString("\n")
146 | }
147 |
148 | /*
149 |
150 | A sample Sudoku board as a List of (position, value) tuples. Positions are in row major format.
151 |
152 | Row-major order
153 | https://en.wikipedia.org/wiki/Row-major_order
154 |
155 | Each element of the list below corresponds to an element in the Sudoku board.
156 | This board has been "flattened" into a row-major list. The first element of each tuple is the "flattened" coordinate in the Sudoku board (explained momentarily), and the second element of each tuple is the value itself.
157 |
158 | First, let's establish syntax for elements of the board
159 |
160 | Given element M_ij,
161 | i is the row and j is the column
162 |
163 | Our row and column indices are 0-indexed, in contrast to the 1-indexed convention of a mathematical matrix.
164 |
165 | A Sudoku board is 9 by 9 elements.
166 |
167 | (0, 5) is placed at M_00
168 | (1, 3) is placed at M_01
169 | (4, 7) is placed at M_04
170 | (12, 1) is placed at M_13
171 |
172 | Here is partial plot of these four elements, with absent elements of the board filled by 0
173 |
174 | 5 3 0 0 7 0 0 0 0
175 | 0 0 0 1 0 0 0 0 0
176 | 0 0 0 0 0 0 0 0 0
177 | 0 0 0 0 0 0 0 0 0
178 | 0 0 0 0 0 0 0 0 0
179 | 0 0 0 0 0 0 0 0 0
180 | 0 0 0 0 0 0 0 0 0
181 | 0 0 0 0 0 0 0 0 0
182 | 0 0 0 0 0 0 0 0 0
183 |
184 | You can print all of `initial` by running `SudokuPreview`
185 | */
186 | val initial = List((0,5),(1,3),(4,7),(9,6),(12,1),(13,9),(14,5),
187 | (19,9),(20,8),(25,6),(27,8),(31,6),(35,3),(36,4),
188 | (39,8),(41,3),(44,1),(45,7),(49,2),(53,6),(55,6),
189 | (60,2),(61,8),(66,4),(67,1),(68,9),(71,5),(76,8),(79,7),(80,9))
190 |
191 | val initial2 = List((0,5),(1,3),(4,7), (12,1),(13,9),(14,5),
192 | (19,9),(20,8), (27,8),(31,6),(35,3),
193 | (39,8), (44,1),(45,7),(49,2), (55,6),
194 | (60,2),(61,8),(66,4), (68,9),(71,5),(76,8),(79,7),(80,9))
195 |
196 |
197 |
198 | }
199 |
200 | object SudokuPreview extends App {
201 | import Sudoku._
202 |
203 |
204 | println("find solutions for this Sudoku board:")
205 |
206 | println(sudokuPlot(initial))
207 |
208 | val test: List[(Int, Int)] = (0 until 81).toList.map(d => (d, d))
209 |
210 | val rows = test.map(tuple => (tuple._1, row(tuple)))
211 |
212 | println("rows")
213 | println(sudokuPlot(rows))
214 |
215 | val columns = test.map(tuple => (tuple._1, column(tuple)))
216 |
217 | println("columns")
218 | println(sudokuPlot(columns))
219 |
220 | val tiles = test.map(tuple => (tuple._1, tile(tuple)))
221 |
222 | println("tiles")
223 | println(sudokuPlot(tiles))
224 |
225 | }
226 |
227 | // TASK 1f
228 | object SudokuSolver extends App {
229 | import Sudoku._
230 |
231 | println("find solutions for this Sudoku board:")
232 | println(sudokuPlot(initial))
233 |
234 | val solutionStrings: Set[String] = sudokuSolve(initial).map(board => sudokuPlot(board))
235 |
236 | solutionStrings.foreach { (solution: String) => println(s"solution \n $solution") }
237 |
238 |
239 |
240 | println("-----------------")
241 |
242 |
243 |
244 | println("find solutions for this Sudoku board (initial2):")
245 | println(sudokuPlot(initial2))
246 |
247 | val solutionStrings2: Set[String] = sudokuSolve(initial2).map(board => sudokuPlot(board))
248 |
249 | solutionStrings2.foreach { (solution: String) => println(s"solution \n $solution") }
250 |
251 |
252 |
253 |
254 |
255 | }
256 |
257 |
258 |
--------------------------------------------------------------------------------
/slides/lecture6.md:
--------------------------------------------------------------------------------
1 |
2 | #Lecture 6: Purely Functional State
3 |
4 |
5 |
6 |
7 |
8 | "Anyone who attempts to generate random numbers by deterministic means is, of course, living in a state of sin."
9 |
10 | -- [John von Neumann](https://en.wikipedia.org/wiki/John_von_Neumann)
11 |
12 | ---
13 |
14 | Just as a mathematical function always calculates the same output for a given input, so does a *referentially transparent* function in functional programming.
15 |
16 | In this lecture we will build an API for pseudo-random number generators that obeys this rule.
17 |
18 | Furthermore, the combinators in our API will prevent the re-generation of the same "random" number. This particular error will not be a concern of our library's user.
19 |
20 | ---
21 |
22 | We start with a [linear congruential generator](https://en.wikipedia.org/wiki/Linear_congruential_generator):
23 |
24 |
25 | !scala
26 | def generateSeed(seed: Long): Long =
27 | (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL
28 |
29 | def generateInt(seed: Long): Int =
30 | (seed >>> 16).toInt
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Defined in `slideCode.lecture6.PseudoRandomNumberGenerator`
39 |
40 |
41 | ---
42 |
43 | `RNG` generates a "random" Integer, and another `RNG`, in a tuple. RNG => (Int, RNG)
44 |
45 |
46 | !scala
47 | case class RNG(seed0: Long) {
48 | def nextInt: (Int, RNG) = {
49 | val seed1: Long = generateSeed(seed0)
50 | val int1: Int = generateInt(seed1)
51 | val rng1: RNG = RNG(seed1)
52 | (int1, rng1)
53 | }
54 | }
55 |
56 |
57 |
58 |
59 |
60 | Defined in `lectureCode.lecture6.PseudoRandomNumberGenerator`
61 |
62 | .notes: `RNG` generates a "random" Integer, and another `RNG`, in a tuple. RNG => (Int, RNG)
63 |
64 | ---
65 |
66 | val simple = RNG(123)
67 | val tup: (Int, RNG) = simple.nextInt
68 | println(tup)
69 | // (47324114,RNG(3101433181802))
70 |
71 |
72 |
73 | ---
74 |
75 | #Exercise
76 |
77 | What does `iterate` do?
78 |
79 | !scala
80 | val rng0 = RNG(123)
81 |
82 | def iterate(iterations: Int)(rng0: RNG):
83 | (Int, RNG) =
84 | (0 to (iterations - 1)).foldLeft((0, rng0)) {
85 | case ((randIntI: Int, rngI: RNG), iter: Int) =>
86 | println(s".. $iter .. $randIntI .. $rngI")
87 | rngI.nextInt
88 | }
89 |
90 | println(iterate(32)(rng0))
91 |
92 |
93 |
94 |
95 |
96 |
97 | Defined in `slideCode.lecture6.RNGPrimitiveExamples`
98 |
99 | ---
100 |
101 | [info] Running slideCode.lecture6.
102 | RNGPrimitiveExamples
103 | iter: 0 randInt: 0 rngI: RNG(123)
104 | iter: 1 randInt: 47324114 rngI: RNG(31014..802)
105 | iter: 2 randInt: -386449838 rngI: RNG(25614..869)
106 | iter: 3 randInt: 806037626 rngI: RNG(52824..4818)
107 | iter: 4 randInt: -1537559018 rngI: RNG(180..38287)
108 | ...
109 | iter: 28 randInt: 1770503168 rngI: RNG(11316..223)
110 | iter: 29 randInt: 265482177 rngI: RNG(1736..462)
111 | iter: 30 randInt: -1627823703 rngI: RNG(179..50073)
112 | iter: 31 randInt: -683498645 rngI: RNG(236..41456)
113 | (-1680880615,RNG(171316784789787))
114 |
115 |
116 |
117 |
118 | ---
119 |
120 | We can begin to manipulate `RNG` to generate different random types:
121 |
122 | !scala
123 | def nonNegativeInt(rng: RNG): (Int, RNG) = {
124 | val (i, r) = rng.nextInt
125 | (if (i < 0) -(i + 1) else i, r)
126 | }
127 | //Uniform on [0,1]
128 | def double(rng: RNG): (Double, RNG) = {
129 | val (i, r) = nonNegativeInt(rng)
130 | (i / (Int.MaxValue.toDouble + 1), r)
131 | }
132 |
133 | ---
134 |
135 | ... or pairs of types.
136 |
137 | !scala
138 | def intDouble(rng: RNG): ((Int, Double), RNG) = {
139 | val (i, r1) = rng.nextInt
140 | val (d, r2) = double(r1)
141 | ((i, d), r2)
142 | }
143 |
144 | However this is tedious and error-prone.
145 |
146 | ---
147 |
148 | #Exercise
149 |
150 | What common pattern is shared between these type signatures?
151 |
152 | !scala
153 | def iterate(iters: Int)(rng0: RNG): (Int, RNG) = ???
154 | def nonNegativeInt(rng: RNG): (Int, RNG) = ???
155 | def double(rng: RNG): (Double, RNG) = ???
156 | def intDouble(rng: RNG): ((Int, Double), RNG) = ???
157 |
158 |
159 | ---
160 |
161 | ---
162 |
163 | We factor this commonality out into a type:
164 |
165 | !scala
166 | type Rand[A] = RNG => (A, RNG)
167 |
168 |
169 | This means we can create a `Rand[Int]` directly from an `RNG` for example:
170 |
171 | !scala
172 | val int: Rand[Int] = _.nextInt
173 |
174 | ---
175 |
176 | # Combinators on `Rand`
177 |
178 | Note that `Rand` is a type, not a class or trait, so we define our combinators in a companion object.
179 |
180 | !scala
181 | object Rand {
182 | // primitive
183 | def unit[A](a: A): Rand[A] = { ... }
184 | def flatMap[A, B](ra: Rand[A])(g: A => Rand[B]):
185 | Rand[B] = { ... }
186 |
187 | // derived
188 | def map[A,B](ra: Rand[A])(f: A => B):
189 | Rand[B] = ???
190 | def map2[A,B,C](ra: Rand[A], rb: Rand[B])
191 | (f: (A, B) => C): Rand[C] = ???
192 | def sequence[A](fs: List[Rand[A]]):
193 | Rand[List[A]] = ???
194 | }
195 |
196 | ---
197 |
198 | #Exercise
199 |
200 | How would you implement `map` for `Rand[A]`?
201 |
202 |
203 | Given `int`, re-implement `double` using `map`. Ignore the edge case.
204 |
205 | ---
206 |
207 | ---
208 |
209 | !scala
210 | def map[A,B](ra: Rand[A])(f: A => B): Rand[B] =
211 | rng => {
212 | val (a, rng1) = ra(rng0)
213 | (f(a), rng1)
214 | }
215 |
216 | ---
217 |
218 |
219 | ---
220 |
221 | !scala
222 | def double: Rand[Double] =
223 | map(int) {
224 | (i: Int) => i.toDouble / Int.MaxValue
225 | }
226 |
227 | Note that no explicit `RNG` value is necessary anywhere in this implementation of `double`.
228 |
229 | ---
230 |
231 | !scala
232 | def flatMap[A, B](ra: Rand[A])(g: A => Rand[B]):
233 | Rand[B] =
234 | rng0 => {
235 | val (a, rng1) = ra(rng0)
236 | g(a)(rng1) //pass the new state along
237 | }
238 |
239 |
240 | ---
241 |
242 | !scala
243 | def map2[A, B, C](ra: Rand[A], rb: Rand[B])
244 | (f: (A, B) => C): Rand[C] =
245 | flatMap(ra) { a =>
246 | map(rb) { b => f(a, b) }
247 | }
248 |
249 | Note that `map2` is a *non-primitive* combinator so we don't have to handle any `RNG` values explicitly.
250 |
251 | ---
252 |
253 | # Passing `RNG` implicitly
254 |
255 | `Rand` passes the `RNG` values for us.
256 |
257 | !scala
258 | val simple = RNG(123)
259 | def double: Rand[Double] = { ... }
260 | def both[A,B](ra: Rand[A], rb: Rand[B]): Rand[(A,B)] =
261 | map2(ra, rb)((_, _))
262 |
263 | println(map2(double, double)(addDoubles)(simple))
264 | // ((0.022037,-0.179954),RNG(256148600186669))
265 |
266 |
267 | If `RNG` were not passed through `map2` correctly, `x` and `y` would be the same value.
268 |
269 | ---
270 |
271 | #Exercise
272 |
273 | How many steps are there from `RNG(123)` to `RNG(256148600186669)`?
274 |
275 | ---
276 |
277 | ---
278 |
279 | scala> val a = RNG(123)
280 | a: RNG = RNG(123)
281 | scala> a.nextInt
282 | res0: (Int, RNG) = (47324114,RNG(3101433181802))
283 | scala> res0._2.nextInt
284 | res1: (Int, RNG) = (-386449838,RNG(256148600186669))
285 |
286 | ---
287 |
288 | #Exercise
289 |
290 | What does `unit` do?
291 |
292 | !scala
293 | def unit[A](a: A): Rand[A] =
294 | rng => (a, rng)
295 |
296 | ---
297 |
298 |
299 | ---
300 |
301 | scala> val u = unit(1)
302 | u: Rand[Int] =
303 | scala> u(a)
304 | res2: (Int, RNG) = (1,RNG(123))
305 |
306 |
307 | What is the use of a random number generator that always returns the same number?
308 |
309 | ---
310 |
311 | ---
312 |
313 | This implementation of `map` makes it a *non-primitive* combinator.
314 |
315 | !scala
316 | def map[A, B](ra: Rand[A])(f: A => B): Rand[B] =
317 | flatMap(ra)((a: A) => unit(f(a)))
318 |
319 | `unit` is one of the primitive combinators required for the Monad typeclass.
320 |
321 | ---
322 |
323 | !scala
324 | def flatMap[A, B](ra: Rand[A])(g: A => Rand[B]):
325 | Rand[B] =
326 | rng => {
327 | val (a, rng1) = ra(rng0)
328 | g(a)(rng1)
329 | }
330 | def map[A,B](ra: Rand[A])(g: A => B):
331 | Rand[B] =
332 | rng => {
333 | val (a, rng1) = ra(rng0)
334 | (g(a), rng1)
335 | }
336 |
337 | ---
338 |
339 | Because `flatMap` can exit its context it can be used to implement recursive methods such as [rejection sampling](https://en.wikipedia.org/wiki/Rejection_sampling):
340 |
341 | !scala
342 | def rejectionSampler[A](ra: Rand[A])
343 | (p: A => Boolean): Rand[A] =
344 | flatMap(ra) { a =>
345 | if (p(a)) unit(a)
346 | else rejectionSampler(ra)(p)
347 | }
348 |
349 | ---
350 |
351 | scala> rejectionSampler(int)(_ % 5 ==0)(a)
352 | res3: (Int, RNG) = (936386220,RNG(61367007330318))
353 |
354 | ---
355 |
356 | Recall that "nesting" is synonymous with `flatMap`, i.e. nested `Rand`s are `flatMapped` together.
357 |
358 | We will make use of `rejectionSampler` extensively in Friday's lab.
359 |
360 | ---
361 |
362 | !scala
363 | def sequence[A](fs: List[Rand[A]]): Rand[List[A]] =
364 | fs.foldRight(unit(List[A]()))
365 | ((f, acc) => map2(f, acc)(_ :: _))
366 |
367 |
368 | ---
369 |
370 | scala> sequence(List(int))(a)
371 | res4: (List(47324114),RNG(3101433181802))
372 | scala> sequence(List(int,int))(a)
373 | res5: (List(47324114, -386449838),RNG(256148600186669))
374 |
375 |
376 | ---
377 |
378 | # Next steps
379 |
380 | `Rand` generalizes to the `State` monad
381 |
382 | With `Rand`, we transformed the transitions between `RNG`s.
383 |
384 | With `State`, we will transform the transitions between generic states
385 |
386 |
387 | Today's "state" was a `RNG`.
388 |
389 | !scala
390 | type Rand[A] = RNG => (A, RNG)
391 |
392 | type State[S,A] = S => (A, S)
393 |
394 | ---
395 |
396 | The meaning of `join` for example in the state monad is to give the outer action an opportunity to get and put the state, then do the same for the inner action, making sure any subsequent actions see the changes made by previous ones.
397 |
398 | !scala
399 | case class State[S,A](run: S => (A, S))
400 | def join[S,A](v1: State[S,State[S,A]]): State[S,A] =
401 | State(s1 => {
402 | val (v2, s2) = v1.run(s1)
403 | v2.run(s2)
404 | })
405 |
406 | ---
407 |
408 | #Homework
409 |
410 | Have a look at `State` in [*Cats*](https://github.com/typelevel/cats).
411 |
--------------------------------------------------------------------------------