├── project ├── build.properties ├── plugins.sbt ├── Common.scala └── Dependencies.scala ├── sbtTutorial ├── multi │ ├── backend │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ ├── Database.scala │ │ │ └── MonteCarlo.scala │ ├── frontend │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ ├── Render.scala │ │ │ └── PrintPi.scala │ └── build.sbt ├── minimal │ ├── project │ │ └── build.properties │ ├── build.sbt │ └── src │ │ └── main │ │ └── scala │ │ └── Hello.scala ├── usingBreeze │ ├── project │ │ └── build.properties │ ├── gaussian_histogram.png │ ├── exponential_histogram.png │ ├── plotting │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── com │ │ │ └── datascience │ │ │ ├── discrete │ │ │ └── PlotBernoulli.scala │ │ │ └── continuous │ │ │ ├── PlotGaussian.scala │ │ │ └── PlotExponential.scala │ ├── distributions │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── com │ │ │ └── datascience │ │ │ ├── continuous │ │ │ ├── Gaussian.scala │ │ │ ├── Poisson.scala │ │ │ └── Exponential.scala │ │ │ └── discrete │ │ │ └── Bernoulli.scala │ └── build.sbt ├── usingCats │ ├── project │ │ └── build.properties │ ├── build.sbt │ └── src │ │ └── main │ │ └── scala │ │ └── UserRepository.scala ├── usingBreezeRefactored │ ├── project │ │ ├── build.properties │ │ ├── Common.scala │ │ └── Dependencies.scala │ ├── foo.png │ ├── gaussian_histogram.png │ ├── exponential_histogram.png │ ├── build.sbt │ ├── plotting │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── com │ │ │ └── datascience │ │ │ ├── discrete │ │ │ └── PlotBernoulli.scala │ │ │ └── continuous │ │ │ ├── PlotGaussian.scala │ │ │ └── PlotExponential.scala │ └── distributions │ │ └── src │ │ └── main │ │ └── scala │ │ └── com │ │ └── datascience │ │ ├── continuous │ │ ├── Gaussian.scala │ │ ├── Poisson.scala │ │ └── Exponential.scala │ │ └── discrete │ │ └── Bernoulli.scala ├── images │ ├── cats_data_kleisli.png │ └── gaussian_histogram_example.png ├── javac │ └── HelloWorld.java ├── antIvy │ └── build.xml └── sbt_command_and_tasks.txt ├── Gemfile ├── slides ├── images │ ├── recursions.png │ ├── sbt │ │ ├── cats_041.png │ │ ├── ivy_depen.png │ │ ├── maven_depen.png │ │ ├── sbt_depen.png │ │ ├── cats_results.png │ │ └── cats_versions.png │ ├── lecture13 │ │ └── free.png │ ├── lecture2 │ │ └── cats.jpg │ ├── lecture3 │ │ ├── list.png │ │ ├── hom-functor.png │ │ ├── collections2.png │ │ ├── collections3-1.png │ │ └── gang-of-four-monads.png │ ├── lecture4 │ │ ├── maybe.png │ │ └── guy_steele.jpg │ ├── lecture4a │ │ └── cats.png │ ├── lecture1 │ │ ├── fib-mat.png │ │ ├── variance.png │ │ ├── fib-process.png │ │ ├── guy_steele.jpg │ │ ├── john_backus.jpg │ │ ├── variance2.png │ │ └── tower_of_hanoi.png │ ├── lecture10 │ │ ├── demorgan1.png │ │ └── demorgan2.png │ ├── lecture11 │ │ └── adjoints.png │ ├── lecture11a │ │ └── burrito.png │ ├── lecture5 │ │ ├── ackermann.png │ │ ├── sine_wave.png │ │ ├── unit_circle.png │ │ └── sine_wave_interpolated.png │ ├── lecture6 │ │ └── von-neumann.jpg │ ├── lecture8 │ │ └── beta_mean.png │ └── lecture10a │ │ └── grouplike_structures.png ├── theme │ ├── css │ │ └── print.css │ ├── js │ │ └── squeezeFrame.js │ └── base.html ├── lecture7.md └── lecture6.md ├── misc └── src │ └── main │ └── scala │ └── com │ └── datascience │ └── education │ └── misc │ └── sudoku │ ├── images │ ├── Sudoku.png │ └── Sudoku_solution.png │ └── Sudoku.scala ├── tutorial └── src │ ├── main │ └── scala │ │ └── com │ │ └── datascience │ │ └── education │ │ └── tutorial │ │ ├── lecture5 │ │ ├── images │ │ │ ├── sine_wave.png │ │ │ ├── sine_wave_stepper.png │ │ │ └── LinearInterpolation.svg.png │ │ ├── ScanAverage.scala │ │ ├── Interpolation.scala │ │ ├── LookandSay.scala │ │ ├── InterpolationExamples.scala │ │ └── QuickSort.scala │ │ ├── lecture2 │ │ ├── images │ │ │ ├── Left-fold-transformation.png │ │ │ └── Right-fold-transformation.png │ │ └── Folding.scala │ │ ├── lecture0 │ │ └── GettingStarted.scala │ │ ├── lecture4a │ │ ├── WebForm.scala │ │ ├── XorList.scala │ │ ├── XorHelpers.scala │ │ ├── WebFormVerifier.scala │ │ ├── ResponseList.scala │ │ ├── SafeDivision.scala │ │ ├── XorErrors.scala │ │ ├── RequestResponse.scala │ │ ├── TraverseOption.scala │ │ ├── XorWebForm.scala │ │ └── TraverseXor.scala │ │ ├── lecture1 │ │ ├── TypeLinearization.scala │ │ ├── Heirloom.scala │ │ ├── ImplicitConversions.scala │ │ ├── Hierarchy.scala │ │ └── TypeClassProblem.scala │ │ ├── lecture4 │ │ ├── Employees.scala │ │ ├── EmptySet.scala │ │ ├── SafeDivision.scala │ │ ├── Covariance.scala │ │ ├── FPOption.scala │ │ └── TraverseOption.scala │ │ └── lecture3 │ │ ├── AsynchronousFactorials.scala │ │ └── Database.scala │ └── test │ └── scala │ └── com │ └── datascience │ └── education │ └── tutorial │ ├── lecture5 │ ├── ScanAverageSpec.scala │ ├── LookandSaySpec.scala │ └── QuickSortSpec.scala │ ├── lecture4 │ ├── FlawedOptionSpec.scala │ ├── EmptySetSpec.scala │ ├── EmployeesSpec.scala │ ├── SafeDivisionSpec.scala │ └── FPOptionSpec.scala │ └── lecture3 │ └── DatabaseSpec.scala ├── .gitignores ├── __default └── master_ignore ├── .gitignore ├── tutorialCommon └── src │ ├── main │ └── scala │ │ └── com │ │ └── datascience │ │ └── education │ │ └── tutorialCommon │ │ └── lecture4 │ │ ├── EmptySetTypeclass.scala │ │ └── EmployeesTypeclass.scala │ └── test │ └── scala │ └── com │ └── datascience │ └── education │ └── tutorialCommon │ └── lecture4 │ ├── CommonEmptySetSpec.scala │ └── CommonEmployeesSpec.scala ├── javascripts └── scale.fix.js ├── .travis.yml ├── params.json ├── README.md ├── README.html ├── index.html ├── stylesheets ├── github-light.css └── styles.css ├── docs └── stylesheets │ ├── github-light.css │ └── styles.css └── LICENSE /project/build.properties: -------------------------------------------------------------------------------- 1 | 2 | sbt.version=0.13.12 -------------------------------------------------------------------------------- /sbtTutorial/multi/backend/src/main/scala/Database.scala: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sbtTutorial/multi/frontend/src/main/scala/Render.scala: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sbtTutorial/minimal/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.11 -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.11 -------------------------------------------------------------------------------- /sbtTutorial/usingCats/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.11 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'github-pages' 4 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.11 -------------------------------------------------------------------------------- /slides/images/recursions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/recursions.png -------------------------------------------------------------------------------- /slides/images/sbt/cats_041.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/sbt/cats_041.png -------------------------------------------------------------------------------- /slides/images/lecture13/free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture13/free.png -------------------------------------------------------------------------------- /slides/images/lecture2/cats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture2/cats.jpg -------------------------------------------------------------------------------- /slides/images/lecture3/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture3/list.png -------------------------------------------------------------------------------- /slides/images/lecture4/maybe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture4/maybe.png -------------------------------------------------------------------------------- /slides/images/lecture4a/cats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture4a/cats.png -------------------------------------------------------------------------------- /slides/images/sbt/ivy_depen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/sbt/ivy_depen.png -------------------------------------------------------------------------------- /slides/images/sbt/maven_depen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/sbt/maven_depen.png -------------------------------------------------------------------------------- /slides/images/sbt/sbt_depen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/sbt/sbt_depen.png -------------------------------------------------------------------------------- /slides/images/lecture1/fib-mat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/fib-mat.png -------------------------------------------------------------------------------- /slides/images/lecture1/variance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/variance.png -------------------------------------------------------------------------------- /slides/images/sbt/cats_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/sbt/cats_results.png -------------------------------------------------------------------------------- /slides/images/sbt/cats_versions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/sbt/cats_versions.png -------------------------------------------------------------------------------- /slides/images/lecture1/fib-process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/fib-process.png -------------------------------------------------------------------------------- /slides/images/lecture1/guy_steele.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/guy_steele.jpg -------------------------------------------------------------------------------- /slides/images/lecture1/john_backus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/john_backus.jpg -------------------------------------------------------------------------------- /slides/images/lecture1/variance2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/variance2.png -------------------------------------------------------------------------------- /slides/images/lecture10/demorgan1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture10/demorgan1.png -------------------------------------------------------------------------------- /slides/images/lecture10/demorgan2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture10/demorgan2.png -------------------------------------------------------------------------------- /slides/images/lecture11/adjoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture11/adjoints.png -------------------------------------------------------------------------------- /slides/images/lecture11a/burrito.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture11a/burrito.png -------------------------------------------------------------------------------- /slides/images/lecture3/hom-functor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture3/hom-functor.png -------------------------------------------------------------------------------- /slides/images/lecture4/guy_steele.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture4/guy_steele.jpg -------------------------------------------------------------------------------- /slides/images/lecture5/ackermann.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture5/ackermann.png -------------------------------------------------------------------------------- /slides/images/lecture5/sine_wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture5/sine_wave.png -------------------------------------------------------------------------------- /slides/images/lecture5/unit_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture5/unit_circle.png -------------------------------------------------------------------------------- /slides/images/lecture6/von-neumann.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture6/von-neumann.jpg -------------------------------------------------------------------------------- /slides/images/lecture8/beta_mean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture8/beta_mean.png -------------------------------------------------------------------------------- /sbtTutorial/images/cats_data_kleisli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/images/cats_data_kleisli.png -------------------------------------------------------------------------------- /slides/images/lecture3/collections2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture3/collections2.png -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/foo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/usingBreezeRefactored/foo.png -------------------------------------------------------------------------------- /slides/images/lecture1/tower_of_hanoi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture1/tower_of_hanoi.png -------------------------------------------------------------------------------- /slides/images/lecture3/collections3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture3/collections3-1.png -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/gaussian_histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/usingBreeze/gaussian_histogram.png -------------------------------------------------------------------------------- /slides/images/lecture3/gang-of-four-monads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture3/gang-of-four-monads.png -------------------------------------------------------------------------------- /sbtTutorial/images/gaussian_histogram_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/images/gaussian_histogram_example.png -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/exponential_histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/usingBreeze/exponential_histogram.png -------------------------------------------------------------------------------- /slides/images/lecture10a/grouplike_structures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture10a/grouplike_structures.png -------------------------------------------------------------------------------- /slides/images/lecture5/sine_wave_interpolated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/slides/images/lecture5/sine_wave_interpolated.png -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/gaussian_histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/usingBreezeRefactored/gaussian_histogram.png -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/exponential_histogram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/sbtTutorial/usingBreezeRefactored/exponential_histogram.png -------------------------------------------------------------------------------- /misc/src/main/scala/com/datascience/education/misc/sudoku/images/Sudoku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/misc/src/main/scala/com/datascience/education/misc/sudoku/images/Sudoku.png -------------------------------------------------------------------------------- /misc/src/main/scala/com/datascience/education/misc/sudoku/images/Sudoku_solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/misc/src/main/scala/com/datascience/education/misc/sudoku/images/Sudoku_solution.png -------------------------------------------------------------------------------- /tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/images/sine_wave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/images/sine_wave.png -------------------------------------------------------------------------------- /tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/images/sine_wave_stepper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/images/sine_wave_stepper.png -------------------------------------------------------------------------------- /tutorial/src/main/scala/com/datascience/education/tutorial/lecture2/images/Left-fold-transformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/tutorial/src/main/scala/com/datascience/education/tutorial/lecture2/images/Left-fold-transformation.png -------------------------------------------------------------------------------- /tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/images/LinearInterpolation.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/tutorial/src/main/scala/com/datascience/education/tutorial/lecture5/images/LinearInterpolation.svg.png -------------------------------------------------------------------------------- /sbtTutorial/minimal/build.sbt: -------------------------------------------------------------------------------- 1 | 2 | 3 | // http://www.scala-sbt.org/0.13/docs/Hello.html 4 | 5 | lazy val root = (project in file(".")). 6 | settings( 7 | name := "hello", 8 | version := "1.0", 9 | scalaVersion := "2.11.8" 10 | ) 11 | -------------------------------------------------------------------------------- /tutorial/src/main/scala/com/datascience/education/tutorial/lecture2/images/Right-fold-transformation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DS12/scala-class/HEAD/tutorial/src/main/scala/com/datascience/education/tutorial/lecture2/images/Right-fold-transformation.png -------------------------------------------------------------------------------- /tutorial/src/main/scala/com/datascience/education/tutorial/lecture0/GettingStarted.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.education.tutorial.lecture0 2 | 3 | 4 | object GettingStarted { 5 | 6 | println("lazy evaluation of the contents of `GettingStarted`") 7 | 8 | def add(i: Int): Int = i+3 9 | 10 | } 11 | -------------------------------------------------------------------------------- /sbtTutorial/javac/HelloWorld.java: -------------------------------------------------------------------------------- 1 | 2 | 3 | // http://introcs.cs.princeton.edu/java/11hello/HelloWorld.java.html 4 | 5 | public class HelloWorld { 6 | 7 | public static void main(String[] args) { 8 | // Prints "Hello, World" to the terminal window. 9 | System.out.println("Hello, World"); 10 | } 11 | 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /.gitignores/__default: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache 6 | .history 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # Scala-IDE specific 16 | .scala_dependencies 17 | .worksheet 18 | 19 | 20 | # Emacs 21 | .#* 22 | *~ 23 | 24 | # Ensime 25 | .ensime 26 | .ensime_cache 27 | 28 | 29 | .DS_Store -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/build.sbt: -------------------------------------------------------------------------------- 1 | 2 | 3 | // emulate this: https://github.com/TrueCar/mleap/blob/master/build.sbt 4 | 5 | // Set sub-project on SBT start: http://stackoverflow.com/a/22240142/1007926 6 | // onLoad in Global := { 7 | // Command.process("project distributions", _: State) 8 | // } compose (onLoad in Global).value 9 | 10 | // Task 5h 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sbtTutorial/minimal/src/main/scala/Hello.scala: -------------------------------------------------------------------------------- 1 | 2 | 3 | // no package necessary 4 | 5 | object Hello extends App { 6 | 7 | println("hello") 8 | 9 | } 10 | 11 | 12 | /* syntactic sugar for: 13 | */ 14 | 15 | object Hello2 { 16 | def main(args: Array[String]): Unit = { 17 | println("hello") 18 | } 19 | } 20 | 21 | 22 | // http://www.scala-lang.org/documentation/getting-started.html 23 | -------------------------------------------------------------------------------- /sbtTutorial/multi/frontend/src/main/scala/PrintPi.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.reporting 2 | 3 | import scala.util.Random 4 | 5 | import com.datascience.approximations.MonteCarlo 6 | 7 | object PrintPi extends App { 8 | 9 | val rand = new Random 10 | 11 | val n = 4096 12 | 13 | val piEstimate: Double = MonteCarlo.pi(n, rand) 14 | 15 | println(s"Pi is estimated to equal $piEstimate with $n Monte Carlo iterations") 16 | 17 | } 18 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/plotting/src/main/scala/com/datascience/discrete/PlotBernoulli.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.discrete 2 | 3 | import breeze.plot._ 4 | import breeze.linalg._ 5 | 6 | // object PlotBernoulli extends App { 7 | // val samples = Bern.sample.take(100000) 8 | 9 | // val f = Figure() 10 | // val p = f.subplot(0) 11 | 12 | // p += hist(samples) 13 | 14 | // f.saveas("bernoulli_histogram.png") 15 | 16 | 17 | // } 18 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/plotting/src/main/scala/com/datascience/discrete/PlotBernoulli.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.discrete 2 | 3 | import breeze.plot._ 4 | import breeze.linalg._ 5 | 6 | // object PlotBernoulli extends App { 7 | // val samples = Bern.sample.take(100000) 8 | 9 | // val f = Figure() 10 | // val p = f.subplot(0) 11 | 12 | // p += hist(samples) 13 | 14 | // f.saveas("bernoulli_histogram.png") 15 | 16 | 17 | // } 18 | -------------------------------------------------------------------------------- /sbtTutorial/usingCats/build.sbt: -------------------------------------------------------------------------------- 1 | 2 | 3 | // http://www.scala-sbt.org/0.13/docs/Hello.html 4 | 5 | lazy val root = (project in file(".")). 6 | settings( 7 | name := "hello", 8 | version := "1.0", 9 | scalaVersion := "2.11.8", 10 | // formatting of dependencies 11 | // http://www.scala-sbt.org/0.13/docs/Library-Dependencies.html#The++key 12 | libraryDependencies += "org.typelevel" % "cats-core_2.11" % "0.5.0" 13 | 14 | ) 15 | 16 | 17 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/distributions/src/main/scala/com/datascience/continuous/Gaussian.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.continuous 2 | 3 | import breeze.stats.distributions.Gaussian 4 | import breeze.stats.mean 5 | 6 | object Gauss { 7 | val zeroToOne = Gaussian(0,1) 8 | 9 | val sample = zeroToOne.sample(100000) 10 | 11 | } 12 | 13 | object GaussExample extends App { 14 | import Gauss._ 15 | 16 | println("Mean of our Gaussian sample: "+mean(sample)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/distributions/src/main/scala/com/datascience/continuous/Gaussian.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.continuous 2 | 3 | import breeze.stats.distributions.Gaussian 4 | import breeze.stats.mean 5 | 6 | object Gauss { 7 | val zeroToOne = Gaussian(0,1) 8 | 9 | val sample = zeroToOne.sample(100000) 10 | 11 | } 12 | 13 | object GaussExample extends App { 14 | import Gauss._ 15 | 16 | println("Mean of our Gaussian sample: "+mean(sample)) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/plotting/src/main/scala/com/datascience/continuous/PlotGaussian.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.continuous 2 | 3 | import breeze.stats.distributions.Gaussian 4 | 5 | import breeze.plot._ 6 | 7 | object PlotGaussian extends App { 8 | val samples = Gauss.sample.take(10000) 9 | 10 | // https://github.com/scalanlp/breeze/wiki/Quickstart#breeze-viz 11 | val f = Figure() 12 | val p = f.subplot(0) 13 | 14 | p += hist(samples) 15 | 16 | 17 | f.saveas("gaussian_histogram.png") 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/plotting/src/main/scala/com/datascience/continuous/PlotGaussian.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.continuous 2 | 3 | import breeze.stats.distributions.Gaussian 4 | 5 | import breeze.plot._ 6 | 7 | object PlotGaussian extends App { 8 | val samples = Gauss.sample.take(10000) 9 | 10 | // https://github.com/scalanlp/breeze/wiki/Quickstart#breeze-viz 11 | val f = Figure() 12 | val p = f.subplot(0) 13 | 14 | p += hist(samples) 15 | 16 | 17 | f.saveas("gaussian_histogram.png") 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/distributions/src/main/scala/com/datascience/discrete/Bernoulli.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.discrete 2 | 3 | import breeze.stats.distributions.Bernoulli 4 | import breeze.stats.meanAndVariance 5 | import breeze.stats.mean 6 | 7 | object Bern { 8 | val bern = new Bernoulli(0.6) 9 | 10 | val sample = bern.sample(1) 11 | } 12 | 13 | object BernExample extends App { 14 | import Bern._ 15 | 16 | val samples = sample.take(100000) 17 | 18 | // println("Mean of our Bernoulli samples: "+mean(samples)) 19 | } 20 | 21 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreeze/plotting/src/main/scala/com/datascience/continuous/PlotExponential.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.continuous 2 | 3 | import breeze.stats.distributions.Exponential 4 | import breeze.plot._ 5 | 6 | 7 | object PlotExponential extends App { 8 | 9 | val samples = Exp.sample.take(1000000) 10 | 11 | // https://github.com/scalanlp/breeze/wiki/Quickstart#breeze-viz 12 | val f = Figure() 13 | val p = f.subplot(0) 14 | 15 | p += hist(samples) 16 | 17 | 18 | 19 | f.saveas("exponential_histogram.png") 20 | 21 | } 22 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/distributions/src/main/scala/com/datascience/discrete/Bernoulli.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.discrete 2 | 3 | import breeze.stats.distributions.Bernoulli 4 | import breeze.stats.meanAndVariance 5 | import breeze.stats.mean 6 | 7 | object Bern { 8 | val bern = new Bernoulli(0.6) 9 | 10 | val sample = bern.sample(1) 11 | } 12 | 13 | object BernExample extends App { 14 | import Bern._ 15 | 16 | val samples = sample.take(100000) 17 | 18 | // println("Mean of our Bernoulli samples: "+mean(samples)) 19 | } 20 | 21 | -------------------------------------------------------------------------------- /sbtTutorial/usingBreezeRefactored/plotting/src/main/scala/com/datascience/continuous/PlotExponential.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.continuous 2 | 3 | import breeze.stats.distributions.Exponential 4 | import breeze.plot._ 5 | 6 | 7 | object PlotExponential extends App { 8 | 9 | val samples = Exp.sample.take(1000000) 10 | 11 | // https://github.com/scalanlp/breeze/wiki/Quickstart#breeze-viz 12 | val f = Figure() 13 | val p = f.subplot(0) 14 | 15 | p += hist(samples) 16 | 17 | 18 | 19 | f.saveas("exponential_histogram.png") 20 | 21 | } 22 | -------------------------------------------------------------------------------- /.gitignores/master_ignore: -------------------------------------------------------------------------------- 1 | 2 | # http://stackoverflow.com/questions/29579546/git-excludesfile-for-a-branch/29583813#29583813 3 | 4 | tutorialAnswer 5 | labAnswer 6 | 7 | 8 | *.class 9 | *.log 10 | 11 | # sbt specific 12 | .cache 13 | .history 14 | .lib/ 15 | dist/* 16 | target/ 17 | lib_managed/ 18 | src_managed/ 19 | project/boot/ 20 | project/plugins/project/ 21 | 22 | 23 | # Scala-IDE specific 24 | .scala_dependencies 25 | .worksheet 26 | 27 | 28 | # Emacs 29 | .#* 30 | *~ 31 | 32 | # Ensime 33 | .ensime 34 | .ensime_cache 35 | 36 | 37 | .DS_Store 38 | -------------------------------------------------------------------------------- /sbtTutorial/antIvy/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache 6 | .history 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # scala class answers 16 | # Each branch cannot have its own .gitignore 17 | # Use this instead: http://stackoverflow.com/a/29583813/1007926 18 | #tutorialAnswer 19 | #labAnswer 20 | 21 | # Scala-IDE specific 22 | .scala_dependencies 23 | .worksheet 24 | 25 | 26 | # Emacs 27 | .#* 28 | *~ 29 | 30 | # Ensime 31 | .ensime 32 | .ensime_cache 33 | 34 | 35 | .DS_Store 36 | 37 | .idea 38 | -------------------------------------------------------------------------------- /sbtTutorial/multi/backend/src/main/scala/MonteCarlo.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.approximations 2 | 3 | import scala.util.Random 4 | 5 | object MonteCarlo { 6 | 7 | def norm(x: Double, y: Double): Double = math.sqrt(x*x + y*y) 8 | 9 | def inCircle(n: Int, rand: Random): Int = 10 | List.fill(n)((rand.nextDouble, rand.nextDouble)). 11 | map { tup => norm(tup._1, tup._2).toInt }. 12 | map { radius => if(radius >= 1) 0 else 1 }. 13 | reduce(_+_) 14 | 15 | 16 | def pi(n: Int, rand: Random): Double = 4*inCircle(n, rand).toDouble/n 17 | 18 | 19 | 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /tutorialCommon/src/main/scala/com/datascience/education/tutorialCommon/lecture4/EmptySetTypeclass.scala: -------------------------------------------------------------------------------- 1 | package com.datascience.education.tutorialCommon.lecture4 2 | 3 | 4 | trait EmptySetTypeclass { 5 | def sumList(l: List[Int]): Int 6 | def prodList(ds: List[Double]): Double 7 | 8 | def sumList2(l: List[Int]): Int 9 | def prodList2(l: List[Double]): Double 10 | 11 | def sum(l: List[Int]): Option[Int] 12 | 13 | def product(l: List[Double]): Option[Double] 14 | 15 | def sum2(l: List[Int]): Option[Int] 16 | 17 | def product2(l: List[Double]): Option[Double] 18 | 19 | } 20 | -------------------------------------------------------------------------------- /javascripts/scale.fix.js: -------------------------------------------------------------------------------- 1 | var metas = document.getElementsByTagName('meta'); 2 | var i; 3 | if (navigator.userAgent.match(/iPhone/i)) { 4 | for (i=0; i: 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\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n \r\n \r\n\r\n\r\n
\r\nLecture 1\r\n \r\nLecture 9\r\n
\r\nLecture 2 \r\n \r\nLecture 10\r\n
\r\nLecture 3 \r\n \r\nLecture 10a\r\n
\r\nLecture 4 \r\n \r\nLecture 11\r\n
\r\nLecture 4a \r\n \r\nLecture 11a\r\n
\r\nLecture 5 \r\n \r\nLecture 12\r\n
\r\nLecture 6 \r\n \r\nLecture 12a\r\n
\r\nLecture 7 \r\n \r\nLecture 13\r\n
\r\nLecture 8 \r\n \r\nLecture 13a\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 | [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?maxAge=2592000)](https://gitter.im/ds12/lobby) 12 | 13 | A link to ScalaDoc will be available in the slide table of contents, above. 14 | 15 | Creative Commons License
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 | [![Scala-Class Build Status](https://travis-ci.org/DS12/scala-class.svg?branch=master)](https://travis-ci.org/DS12/scala-class) 21 | 22 | `DS12/scala-class-private` build status 23 | 24 | [![Scala-Class-Private Build Status](https://travis-ci.com/DS12/scala-class-private.svg?token=jyR81STLm55Uqgud7sk1&branch=master)](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 |

Gitter

20 |

A link to ScalaDoc will be available in the slide table of contents, above.

21 |

Creative Commons License
Functional Scala for Data Engineers is licensed under a Creative Commons CC0 1.0 Universal License.

22 |

DS12/scala-class build status

23 |

Scala-Class Build Status

24 |

DS12/scala-class-private build status

25 |

Scala-Class-Private Build Status

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 |
18 |

Scala

19 |

Lecture slides, labs, and tutorials from our 8-week Scala course.

20 | 21 |

View the Project on GitHub DS12/scala-class

22 | 23 | 24 | 29 |
30 |
31 |

32 | Lecture Slides

33 | 34 | 35 | 36 | 39 | 42 | 43 | 44 | 47 | 50 | 51 | 52 | 55 | 58 | 59 | 60 | 63 | 66 | 67 | 68 | 71 | 74 | 75 | 76 | 79 | 82 | 83 | 84 | 87 | 90 | 91 | 92 | 95 | 98 | 99 | 100 | 103 | 106 | 107 |
37 | Lecture 1 38 | 40 | Lecture 9 41 |
45 | Lecture 2 46 | 48 | Lecture 10 49 |
53 | Lecture 3 54 | 56 | Lecture 10a 57 |
61 | Lecture 4 62 | 64 | Lecture 11 65 |
69 | Lecture 4a 70 | 72 | Lecture 11a 73 |
77 | Lecture 5 78 | 80 | Lecture 12 81 |
85 | Lecture 6 86 | 88 | Lecture 12a 89 |
93 | Lecture 6a 94 | 96 | Lecture 13 97 |
101 | Lecture 8 102 | 104 | Lecture 13a 105 |
108 | 109 |

Lectures 4-13a are numbered according to their corresponding chapter in Functional Programming in Scala.

110 |
111 |
112 |

This project is maintained by DS12

113 |

Hosted on GitHub Pages — Theme by orderedlist

114 |
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 |
89 |
90 |
91 |
92 | {% for slide in slides %} 93 | 94 |
95 |
96 |
97 | {% if slide.header %} 98 |
{{ slide.header }}
99 | {% endif %} 100 | {% if slide.content %} 101 |
{{ slide.content }}
102 | {% endif %} 103 |
104 |
105 |

Presenter Notes

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 | 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 | --------------------------------------------------------------------------------