├── .gitignore ├── LICENSE ├── README.md ├── build.sbt ├── exercise1_scala_101 └── src │ ├── main │ └── scala │ │ └── exercise1 │ │ ├── Classes.scala │ │ ├── Functions.scala │ │ ├── HigherOrder.scala │ │ └── PatternMatching.scala │ └── test │ └── scala │ └── exercise1 │ ├── ClassesSolution.scala │ ├── FunctionsSolution.scala │ ├── FunctionsSpec.scala │ ├── HigherOrderSolution.scala │ ├── HigherOrderSpec.scala │ ├── PatternMatchingSolution.scala │ └── PatternMatchingSpec.scala ├── exercise2_fp_101 └── src │ ├── main │ └── scala │ │ └── exercise2 │ │ ├── Compositions.scala │ │ ├── RecursiveData.scala │ │ └── RecursiveFunctions.scala │ └── test │ └── scala │ └── exercise2 │ ├── CompositionsSolution.scala │ ├── CompositionsSpec.scala │ ├── RecursiveDataSolution.scala │ ├── RecursiveDataSpec.scala │ ├── RecursiveFunctionsSolution.scala │ └── RecursiveFunctionsSpec.scala ├── exercise3_std_lib └── src │ ├── main │ └── scala │ │ └── exercise3 │ │ ├── Adts.scala │ │ ├── Maps.scala │ │ ├── Sequence.scala │ │ └── Strings.scala │ └── test │ └── scala │ └── exercise3 │ ├── AdtsSolution.scala │ ├── AdtsSpec.scala │ ├── MapsSolution.scala │ ├── MapsSpec.scala │ ├── SequenceSolution.scala │ ├── SequenceSpec.scala │ ├── StringsSolution.scala │ └── StringsSpec.scala ├── exercise4_typeclasses_101 └── src │ ├── main │ └── scala │ │ └── exercise4 │ │ └── Typeclasses.scala │ └── test │ └── scala │ └── exercise4 │ ├── TypeclassSolution.scala │ └── TypeclassSpec.scala ├── exercise5_typeclasses_incarnations └── src │ ├── main │ └── scala │ │ └── exercise5 │ │ ├── Core.scala │ │ ├── Kernel.scala │ │ └── MonadTransformers.scala │ └── test │ └── scala │ └── exercise5 │ ├── CoreSolution.scala │ ├── CoreSpec.scala │ ├── KernelSolution.scala │ ├── KernelSpec.scala │ ├── MonadTransformersSolution.scala │ └── MonadTransformersSpec.scala ├── exercise7_io ├── src │ └── main │ │ └── scala │ │ └── App.scala └── user.db ├── introduction ├── img │ ├── christianstein.jpg │ ├── davidkrentzlin.jpg │ ├── floriansachse.png │ ├── paulheymann.jpg │ ├── sbt_logo.svg │ └── scala_fp.svg ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── Introduction.scala ├── lecture1_scala_101 ├── img │ ├── java-logo.svg │ ├── jvm.svg │ ├── learning_curve.jpg │ ├── principles.svg │ ├── scala-ecosystem.svg │ ├── scala-paper.png │ └── targets.svg ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── Scala101Lecture.scala ├── lecture2_fp_101 ├── img │ ├── indirect_rec.svg │ ├── list.svg │ ├── single_multi_rec.svg │ └── stack.svg ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── FP101Lecture.scala ├── lecture3_std_lib ├── StdLibLecture.txt ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ ├── REPLesent.scala │ └── StdLibLecture.scala ├── lecture4_typeclasses_101 ├── img │ └── yeah.gif ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── TypeClasses101Lecture.scala ├── lecture5_typeclasses_incarnations ├── img │ ├── cats-logo.png │ └── monoids-monoids-everywhere.jpg ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── TypeClassesIncarnationsLecture.scala ├── lecture6_side_effects ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── SideEffectsLecture.scala ├── lecture7_io ├── index.html ├── jsdeps.js ├── presentation.js └── src │ └── main │ └── scala │ └── IOLecture.scala ├── lectures-shared └── src │ └── main │ └── scala │ └── PresentationUtil.scala ├── project ├── build.properties └── plugins.sbt ├── reveal ├── css │ ├── presentation.css │ ├── reveal.css │ ├── reveal.scss │ └── theme │ │ ├── source │ │ └── white.scss │ │ ├── template │ │ ├── mixins.scss │ │ ├── settings.scss │ │ └── theme.scss │ │ └── white.css ├── img │ ├── dark-logo.svg │ └── logo.svg ├── js │ └── reveal.js ├── lib │ ├── css │ │ └── vscode.css │ ├── font │ │ ├── league-gothic │ │ │ ├── LICENSE │ │ │ ├── league-gothic.css │ │ │ ├── league-gothic.eot │ │ │ ├── league-gothic.ttf │ │ │ └── league-gothic.woff │ │ └── source-sans-pro │ │ │ ├── LICENSE │ │ │ ├── source-sans-pro-italic.eot │ │ │ ├── source-sans-pro-italic.ttf │ │ │ ├── source-sans-pro-italic.woff │ │ │ ├── source-sans-pro-regular.eot │ │ │ ├── source-sans-pro-regular.ttf │ │ │ ├── source-sans-pro-regular.woff │ │ │ ├── source-sans-pro-semibold.eot │ │ │ ├── source-sans-pro-semibold.ttf │ │ │ ├── source-sans-pro-semibold.woff │ │ │ ├── source-sans-pro-semibolditalic.eot │ │ │ ├── source-sans-pro-semibolditalic.ttf │ │ │ ├── source-sans-pro-semibolditalic.woff │ │ │ └── source-sans-pro.css │ └── js │ │ ├── classList.js │ │ ├── head.min.js │ │ └── html5shiv.js └── plugin │ ├── highlight │ └── highlight.js │ ├── math │ └── mathjax.js │ └── zoom-js │ └── zoom.js └── xtictactoe ├── README.md └── src └── main └── scala └── xtictactoe ├── Console.scala ├── Game.scala └── Logic.scala /.gitignore: -------------------------------------------------------------------------------- 1 | # emacs 2 | *.*~ 3 | 4 | # sbt 5 | target 6 | project/target 7 | project/project -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Code License 2 | 3 | MIT License 4 | 5 | Copyright (c) 2018 Paul Heymann 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | 26 | # Slides License 27 | 28 | Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) 29 | 30 | ## Summary 31 | ### Share 32 | copy and redistribute the material in any medium or format 33 | 34 | ### Adapt 35 | remix, transform, and build upon the material 36 | 37 | ### Attribution 38 | You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. 39 | 40 | ### NonCommercial 41 | You may not use the material for commercial purposes. 42 | 43 | ## Complete License 44 | The complete license can be found here https://creativecommons.org/licenses/by-nc/4.0/legalcode. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: CC BY-NC 4.0](https://licensebuttons.net/l/by-nc/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc/4.0/) 2 | 3 | # Scala Summer School 4 | This repository contains the course materials we used for a five days workshop on Functional Programming in Scala. You find lecture slides, exercises, and projects here. Taking a look at the [Table of Contents](#table-of-contents) shows you the range of topics you will cover. The workshop starts with a basic introduction into [Scala](https://www.scala-lang.org/) and Functional Programming and continues into more advanced concepts like type-classes and how to handle side-effects. 5 | 6 | Since all lecture slides are based on [reveal.js](https://github.com/hakimel/reveal.js) you have to use your arrow keys to navigate. And you have to navigate in two dimensions. Chapters are organized in columns, slides in rows. To get an overview of all slides within a lecture press ESC. 7 | 8 | ## Table of Contents 9 | 1. [Introduction](https://scalasummerschool.github.io/lectures/introduction) 10 | 2. [Scala 101](https://scalasummerschool.github.io/lectures/lecture1_scala_101) 11 | 3. [Functional Programming 101](https://scalasummerschool.github.io/lectures/lecture2_fp_101) 12 | 4. [Standard Library](https://scalasummerschool.github.io/lectures/lecture3_std_lib) 13 | 5. [Midterm Project](https://www.github.com/scalasummerschool/lectures/tree/master/xtictactoe) 14 | 6. [Type Classes](https://scalasummerschool.github.io/lectures/lecture4_typeclasses_101) 15 | 7. [Type Class Incarnations](https://scalasummerschool.github.io/lectures/lecture5_typeclasses_incarnations) 16 | 8. [Side Effects](https://scalasummerschool.github.io/lectures/lecture6_side_effects) 17 | 9. [IO](https://scalasummerschool.github.io/lectures/lecture7_io) 18 | 10. [Final Project](https://www.github.com/scalasummerschool/tictactoe) 19 | 20 | ## Follow-Up Materials 21 | This workshop will give you a first introduction to Scala and the Functional Programming paradigm. If you want to go into greater details you might be interested in the free books provided by [underscore](https://underscore.io/training/). 22 | 23 | ## Preparations 24 | ### Mandatory 25 | Before you can start you have to install the following tools: 26 | 1. JDK 8 or newer (I think the latest stable version is 10) 27 | 2. [Scala](https://www.scala-lang.org/download/) 28 | 3. [SBT](https://www.scala-sbt.org/download.html) - it is the build-tool we will use 29 | 4. `git clone` this repository 30 | 31 | You have to install it in that order because each tool relies on the tools installed before. 32 | 33 | ### Optional 34 | - [Ammonite](http://ammonite.io/#Ammonite-REPL) - this is an enhanced REPL which makes it easier to fiddle around with some code. But you don't need it. Scala comes already with a REPL. But it isn't that great :). 35 | 36 | ## SBT 37 | Here, you find a list of useful SBT commands: 38 | 39 | ```bash 40 | # start SBT in the repo root and keep it running 41 | sbt 42 | 43 | # show all sub-projects 44 | sbt> projects 45 | 46 | # switch to a sub-project 47 | sbt> project 48 | 49 | # compile 50 | sbt> compile 51 | 52 | # compile on every file change 53 | sbt> ~compile 54 | 55 | # run test 56 | sbt> test 57 | 58 | # run a certain test 59 | sbt> test:testOnly 60 | ``` 61 | 62 | If you need some more information take a look into the [introduction slides](https://scalasummerschool.github.io/lectures/introduction/). 63 | 64 | ## Build the Lectures 65 | To build a lecture go through the following steps: 66 | 67 | ```bash 68 | $> cd /path/to/repo 69 | $> sbt 70 | # select one of the shown lecture 71 | sbt> projects 72 | sbt> project scala101-lecture 73 | 74 | # minimum JS optimizations 75 | sbt> fastCompile 76 | 77 | # maximum JS optimizations (Github page) 78 | sbt> fullCompile 79 | sbt> exit 80 | 81 | # opens lecture in the default browser 82 | $> open lecture1_scala_101/index.html 83 | ``` 84 | 85 | ## License 86 | As stated in the [License file](https://github.com/scalasummerschool/lectures/blob/master/LICENSE) all lecture slides are provided under Creative Commons BY-NC 4.0. The exercise code is released under an MIT license. 87 | 88 | The following people are authors of the content in this repository: 89 | - Paul Heymann 90 | - David Krentzlin 91 | - Christian Stein 92 | - Florian Sachse 93 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | 2 | val common = Seq( 3 | version := "DEVELOP", 4 | scalaVersion := "2.12.6" 5 | ) 6 | 7 | val reactV = "16.2.0" 8 | 9 | lazy val lectureCommon = Seq( 10 | libraryDependencies ++= Seq( 11 | "com.github.japgolly.scalajs-react" %%% "core" % "1.2.3", 12 | "org.scala-js" %%% "scalajs-dom" % "0.9.2" 13 | ), 14 | jsDependencies ++= Seq( 15 | "org.webjars.bower" % "react" % "15.2.1" / "react-with-addons.js" minified "react-with-addons.min.js" commonJSName "React", 16 | "org.webjars.bower" % "react" % "15.2.1" / "react-dom.js" minified "react-dom.min.js" dependsOn "react-with-addons.js" commonJSName "ReactDOM" 17 | ), 18 | 19 | scalaJSUseMainModuleInitializer := true 20 | ) 21 | 22 | val copyFast = taskKey[Unit]("Copy fast optimized JS presentation.") 23 | 24 | def copyFastImpl(project: String) = Seq( 25 | copyFast := { 26 | IO.copyFile( 27 | target.value / "scala-2.12" / s"$project-fastopt.js", 28 | baseDirectory.value / "presentation.js" 29 | ) 30 | IO.copyFile( 31 | target.value / "scala-2.12" / s"$project-jsdeps.js", 32 | baseDirectory.value / "jsdeps.js" 33 | ) 34 | } 35 | ) 36 | 37 | val copyFull = taskKey[Unit]("Copy fully optimized JS presentation.") 38 | 39 | def copyFullImpl(project: String) = Seq( 40 | copyFull := { 41 | IO.copyFile( 42 | target.value / "scala-2.12" / s"$project-opt.js", 43 | baseDirectory.value / "presentation.js" 44 | ) 45 | IO.copyFile( 46 | target.value / "scala-2.12" / s"$project-jsdeps.min.js", 47 | baseDirectory.value / "jsdeps.js" 48 | ) 49 | } 50 | ) 51 | 52 | lazy val root = project 53 | .in(file(".")) 54 | .aggregate( 55 | `introduction`, 56 | `lectures-shared`, `exercises-shared`, 57 | `scala101-lecture`, `scala101-exercises`, 58 | `fp101-lecture`, `fp101-exercises`, 59 | `std-lib-lecture`, `std-lib-exercises`, 60 | `typeclasses-101-lecture`, `typeclasses-101-exercises`, 61 | `typeclasses-incarnations-lecture`, `typeclasses-incarnations-exercises`, 62 | `side-effects-lecture`, 63 | `io-lecture`, `io-exercises`, 64 | `xtictactoe` 65 | ) 66 | .settings( 67 | sourceDirectories in Compile := Nil, 68 | sourceDirectories in Test := Nil 69 | ) 70 | 71 | lazy val introduction = project 72 | .in(file("introduction")) 73 | .enablePlugins(ScalaJSPlugin) 74 | .settings( 75 | common, 76 | lectureCommon, 77 | copyFastImpl("introduction"), 78 | copyFullImpl("introduction"), 79 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 80 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 81 | ) 82 | .settings( 83 | name := "introduction", 84 | ) 85 | .dependsOn(`lectures-shared`) 86 | 87 | lazy val `lectures-shared` = project 88 | .in(file("lectures-shared")) 89 | .enablePlugins(ScalaJSPlugin) 90 | .settings( 91 | common, 92 | lectureCommon 93 | ) 94 | 95 | lazy val `exercises-shared` = project 96 | .in(file("exercises-shared")) 97 | .settings(common) 98 | .settings( 99 | libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % "test" 100 | ) 101 | 102 | lazy val `scala101-lecture` = project 103 | .in(file("lecture1_scala_101")) 104 | .enablePlugins(ScalaJSPlugin) 105 | .settings( 106 | common, 107 | lectureCommon, 108 | copyFastImpl("scala101-lecture"), 109 | copyFullImpl("scala101-lecture"), 110 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 111 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 112 | ) 113 | .settings( 114 | name := "scala101-lecture", 115 | ) 116 | .dependsOn(`lectures-shared`) 117 | 118 | lazy val `scala101-exercises` = project 119 | .in(file("exercise1_scala_101")) 120 | .settings(common) 121 | .settings( 122 | name := "scala101-exercises" 123 | ) 124 | .dependsOn(`exercises-shared` % "compile->compile;test->test") 125 | 126 | lazy val `fp101-lecture` = project 127 | .in(file("lecture2_fp_101")) 128 | .enablePlugins(ScalaJSPlugin) 129 | .settings( 130 | common, 131 | lectureCommon, 132 | copyFastImpl("fp101-lecture"), 133 | copyFullImpl("fp101-lecture"), 134 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 135 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 136 | ) 137 | .settings( 138 | name := "fp101-lecture" 139 | ) 140 | .dependsOn(`lectures-shared`) 141 | 142 | lazy val `fp101-exercises` = project 143 | .in(file("exercise2_fp_101")) 144 | .settings(common) 145 | .settings( 146 | name := "fp101-exercises" 147 | ) 148 | .dependsOn(`exercises-shared` % "compile->compile;test->test") 149 | 150 | lazy val `std-lib-lecture` = project 151 | .in(file("lecture3_std_lib")) 152 | .enablePlugins(ScalaJSPlugin) 153 | .settings( 154 | common, 155 | lectureCommon, 156 | copyFastImpl("std-lib-lecture"), 157 | copyFullImpl("std-lib-lecture"), 158 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 159 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 160 | ) 161 | .settings( 162 | name := "std-lib-lecture", 163 | excludeFilter in unmanagedSources := "REPLesent.scala" 164 | ) 165 | .dependsOn(`lectures-shared`) 166 | 167 | lazy val `std-lib-exercises` = project 168 | .in(file("exercise3_std_lib")) 169 | .settings(common) 170 | .settings( 171 | name := "std-lib-exercises" 172 | ) 173 | .dependsOn(`exercises-shared` % "compile->compile;test->test") 174 | 175 | lazy val `typeclasses-101-lecture` = project 176 | .in(file("lecture4_typeclasses_101")) 177 | .enablePlugins(ScalaJSPlugin) 178 | .settings( 179 | common, 180 | lectureCommon, 181 | copyFastImpl("typeclasses-101-lecture"), 182 | copyFullImpl("typeclasses-101-lecture"), 183 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 184 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 185 | ) 186 | .settings( 187 | name := "typeclasses-101-lecture" 188 | ) 189 | .dependsOn(`lectures-shared`) 190 | 191 | lazy val `typeclasses-101-exercises` = project 192 | .in(file("exercise4_typeclasses_101")) 193 | .settings(common) 194 | .settings( 195 | name := "typeclasses-101-exercises" 196 | ) 197 | .dependsOn(`exercises-shared` % "compile->compile;test->test") 198 | 199 | lazy val `typeclasses-incarnations-lecture` = project 200 | .in(file("lecture5_typeclasses_incarnations")) 201 | .enablePlugins(ScalaJSPlugin) 202 | .settings( 203 | common, 204 | lectureCommon, 205 | copyFastImpl("typeclasses-incarnations-lecture"), 206 | copyFullImpl("typeclasses-incarnations-lecture"), 207 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 208 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 209 | ).settings( 210 | name := "typeclasses-incarnations-lecture" 211 | ).dependsOn(`lectures-shared`) 212 | 213 | lazy val `typeclasses-incarnations-exercises` = project 214 | .in(file("exercise5_typeclasses_incarnations")) 215 | .settings(common) 216 | .settings( 217 | name := "typeclasses-incarnations-exercises", 218 | libraryDependencies += "org.typelevel" %% "cats-core" % "1.4.0" % Compile, 219 | addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7") 220 | ) 221 | .dependsOn(`exercises-shared` % "compile->compile;test->test") 222 | 223 | lazy val `side-effects-lecture` = project 224 | .in(file("lecture6_side_effects")) 225 | .enablePlugins(ScalaJSPlugin) 226 | .settings( 227 | common, 228 | lectureCommon, 229 | copyFastImpl("side-effects-lecture"), 230 | copyFullImpl("side-effects-lecture"), 231 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 232 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 233 | ) 234 | .settings( 235 | name := "side-effects-lecture", 236 | ) 237 | .dependsOn(`lectures-shared`) 238 | 239 | lazy val `io-lecture` = project 240 | .in(file("lecture7_io")) 241 | .enablePlugins(ScalaJSPlugin) 242 | .settings( 243 | common, 244 | lectureCommon, 245 | copyFastImpl("io-lecture"), 246 | copyFullImpl("io-lecture"), 247 | addCommandAlias("fastCompile", "; fastOptJS; copyFast"), 248 | addCommandAlias("fullCompile", "; fullOptJS; copyFull") 249 | ) 250 | .settings( 251 | name := "io-lecture", 252 | ) 253 | .dependsOn(`lectures-shared`) 254 | 255 | lazy val `io-exercises` = project 256 | .in(file("exercise7_io")) 257 | .settings(common) 258 | .settings( 259 | name := "io-exercises", 260 | libraryDependencies ++= Seq( 261 | "org.typelevel" %% "cats-core" % "1.4.0" % Compile, 262 | "org.typelevel" %% "cats-effect" % "1.0.0" % Compile 263 | ) 264 | ) 265 | 266 | lazy val `xtictactoe` = project 267 | .in(file("xtictactoe")) 268 | .settings(common) 269 | .settings( 270 | name := "xtictactoe", 271 | libraryDependencies += "org.typelevel" %% "cats-effect" % "1.0.0" 272 | ) 273 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/main/scala/exercise1/Classes.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | /* This task has no tests. It is an exercise for you to write different class structures. 4 | * 5 | * a) Create a class Animal which has the following fields: 6 | * - name: String (name of the species) 7 | * - species: String (e.g. mammal or reptile) 8 | * - food: String 9 | * 10 | * Syntax: class MyClass(val publicField: Int, privateField: String) { 11 | * // put other fields and methods here 12 | * } 13 | * 14 | * b) Create an Companion Object for Animal and add the following instances as fields: 15 | * - cat, mammal, meat 16 | * - parrot, bird, vegetables 17 | * - goldfish, fish, plants 18 | * 19 | * Syntax: object MyClass { 20 | * // put your static fields and methods here 21 | * } 22 | * 23 | * c) Add the following method to Animals: 24 | * def eats(food: String): Boolean 25 | * 26 | * which checks if an animal eats the given food 27 | * 28 | * d) Redefine your class Animal as trait and create case class instances for Mammals, Birds, and Fishs. 29 | * do you still need the field `species`? 30 | * 31 | * e) Add the following functions to Animals Companion Object: 32 | * def knownAnimal(name: String): Boolean // true if it is the name of one of the three animals we know from (b) 33 | * def apply(name: String): Option[Animal] // returns one of the three animals matching the name (Some) or nothing (None), see Option below 34 | * 35 | * f) Create a trait Food with the following case objects: 36 | * - Meat 37 | * - Vegetables 38 | * - Plants 39 | * and add it to Animal definition. Also add a companion object with an apply method: 40 | * def apply(food: String): Option[Food] 41 | */ 42 | 43 | sealed trait Option[A] { 44 | 45 | def isEmpty: Boolean 46 | } 47 | case class Some[A](a: A) extends Option[A] { 48 | val isEmpty = false 49 | } 50 | case class None[A]() extends Option[A] { 51 | val isEmpty = true 52 | } 53 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/main/scala/exercise1/Functions.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | /** Write your solutions as stand-alone functions. 4 | * 5 | * Syntax: 6 | * // method 7 | * def myFunction(param0: Int, param1: String): Double = // body expression 8 | * 9 | * // value 10 | * val myFunction: (Int, String) => Double (param0, param1) => // body expression 11 | */ 12 | object Functions { 13 | 14 | /* a) Write a function which calculates the area of a 15 | * circle r^2 * Math.PI 16 | */ 17 | 18 | 19 | 20 | // apply your solution-function from (a) here, DO NOT change the signature 21 | def testCircle(r: Double): Double = r 22 | 23 | /* b) Write a curried function which calculates the area of a 24 | * rectangle a * b. 25 | */ 26 | 27 | 28 | 29 | // apply your solution-function from (b) here, DO NOT change the signature 30 | def testRectangleCurried(a: Double, b: Double): Double = a 31 | 32 | // c) Write a uncurried `rectangle` function. 33 | 34 | 35 | 36 | // apply your solution-function from (c) here, DO NOT change the signature 37 | def testRectangleUc(a: Double, b: Double): Double = a 38 | } 39 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/main/scala/exercise1/HigherOrder.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | /** Write your solutions in stand-alone function. */ 4 | object HigherOrder { 5 | 6 | val plus: (Int, Int) => Int = _ + _ 7 | val multiply: (Int, Int) => Int = _ * _ 8 | 9 | /* a) Write a function which takes an `f: (Int, Int) => Int`, its parameters `a` and `b` 10 | * and a multiplication factor `n` and returns n * f(a, b). Let's call it `nTimes`. 11 | */ 12 | 13 | 14 | 15 | // apply your solution-function from (a) here, DO NOT change the signature 16 | def testNTimes(f: (Int, Int) => Int, a: Int, b: Int, n: Int): Int = n 17 | 18 | /* b) Write an anonymous function, a function without identifier ((a, b) => ???) for `nTimes` which 19 | * does the following: 20 | * if (a > b) a else b 21 | */ 22 | def testAnonymousNTimes(a: Int, b: Int, n: Int): Int = a 23 | } 24 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/main/scala/exercise1/PatternMatching.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | /** Write your solution as stand-alone function. 4 | * 5 | * Syntax: 6 | * val a: Int = ??? 7 | * 8 | * a match { 9 | * case 0 => true 10 | * case _ => false 11 | * } 12 | */ 13 | object PatternMatching { 14 | 15 | sealed trait Hand 16 | case object Rock extends Hand 17 | case object Paper extends Hand 18 | case object Scissor extends Hand 19 | 20 | sealed trait Result 21 | case object Win extends Result 22 | case object Lose extends Result 23 | case object Draw extends Result 24 | 25 | sealed trait Food 26 | case object Meat extends Food 27 | case object Vegetables extends Food 28 | case object Plants extends Food 29 | 30 | sealed trait Animal { 31 | 32 | val name: String 33 | val food: Food 34 | } 35 | case class Mammal(name: String, food: Food, weight: Int) extends Animal 36 | case class Fish(name: String, food: Food) extends Animal 37 | case class Bird(name: String, food: Food) extends Animal 38 | 39 | 40 | 41 | /* a) If value is: 42 | * 1 => "it is one" 43 | * 2 => "it is two" 44 | * 3 => "it is three" 45 | * otherwise => "what's that" 46 | */ 47 | 48 | 49 | 50 | // apply your solution-function from (a) here, DO NOT change the signature 51 | def testIntToString(value: Int): String = value.toString 52 | 53 | /* b) `value` is true if it is: 54 | * "max" or "Max 55 | * "moritz" or "Moritz" 56 | */ 57 | 58 | 59 | 60 | // apply your solution-function from (b) here, DO NOT change the signature 61 | def testIsMaxAndMoritz(value: String): Boolean = false 62 | 63 | // c) Is `value` even (use guards) 64 | 65 | 66 | 67 | // apply your solution-function from (c) here, DO NOT change the signature 68 | def testIsEven(value: Int): Boolean = false 69 | 70 | 71 | 72 | /* d) Rock paper scissors (see classes below): 73 | * 1. rock beats scissor 74 | * 2. scissor beats paper 75 | * 3. paper beats rock 76 | * Does player `a` win? 77 | */ 78 | 79 | 80 | 81 | // apply your solution-function from (d) here, DO NOT change the signature 82 | def testWinsA(a: Hand, b: Hand): Result = Draw 83 | 84 | // REMARK: Use the definition of Animals below 85 | 86 | // e) Extract the weight of a Mammal else return -1. 87 | 88 | 89 | 90 | // apply your solution-function from (e) here, DO NOT change the signature 91 | def testExtractMammalWeight(animal: Animal): Int = 0 92 | 93 | // f) Update the food of Fishes and Birds to Plants, keep Mammels unchanged. 94 | 95 | 96 | 97 | // apply your solution-function from (f) here, DO NOT change the signature 98 | def testUpdateFood(animal: Animal): Animal = animal 99 | 100 | } 101 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/ClassesSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | object ClassesSolution { 4 | 5 | sealed trait Food 6 | case object Meat extends Food 7 | case object Vegetables extends Food 8 | case object Plants extends Food 9 | 10 | object Food { 11 | 12 | def apply(food: String): Option[Food] = 13 | if (food == "meat") Some(Meat) 14 | else if (food == "vegetables") Some(Vegetables) 15 | else if (food == "plants") Some(Plants) 16 | else None() 17 | } 18 | 19 | sealed trait Animal { 20 | 21 | val name: String 22 | val food: Food 23 | 24 | def eats(food: Food): Boolean = food == this.food 25 | } 26 | 27 | case class Mammal(name: String, food: Food) extends Animal 28 | case class Bird(name: String, food: Food) extends Animal 29 | case class Fish(name: String, food: Food) extends Animal 30 | 31 | object Animal { 32 | 33 | val cat = Mammal("cat", Meat) 34 | val parrot = Bird("parrot", Vegetables) 35 | val goldfish = Fish("goldfish", Plants) 36 | 37 | def knownAnimal(name: String): Boolean = 38 | name == "cat" || name == "parrot" || name == "goldfish" 39 | 40 | def apply(name: String): Option[Animal] = 41 | if (name == cat.name) Some(cat) 42 | else if (name == parrot.name) Some(parrot) 43 | else if (name == goldfish.name) Some(goldfish) 44 | else None() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/FunctionsSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | object FunctionsSolution { 4 | 5 | def circle(r: Double): Double = r * r * Math.PI 6 | 7 | def rectangleCurried(a: Double)(b: Double): Double = a * b 8 | 9 | def rectangleUncurried(a: Double, b: Double): Double = rectangleCurried(a)(b) 10 | 11 | val rectangleUnderscore: (Double, Double) => Double = _ * _ 12 | } 13 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/FunctionsSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | 6 | object FunctionsSpec extends Properties("functions") { 7 | 8 | property("circle") = forAll { r: Double => 9 | FunctionsSolution.circle(r) ?= Functions.testCircle(r) 10 | } 11 | 12 | property("rectangle curried") = forAll { (a: Double, b: Double) => 13 | FunctionsSolution.rectangleCurried(a)(b) ?= Functions.testRectangleCurried(a, b) 14 | } 15 | 16 | property("rectangle uncurried") = forAll { (a: Double, b: Double) => 17 | FunctionsSolution.rectangleUncurried(a, b) ?= Functions.testRectangleUc(a, b) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/HigherOrderSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | object HigherOrderSolution { 4 | 5 | def nTimes(f: (Int, Int) => Int, a: Int, b: Int, n: Int): Int = n * f(a, b) 6 | } 7 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/HigherOrderSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | 6 | object HigherOrderSpec extends Properties("higher-order") { 7 | 8 | property("n-times (plus, multiply)") = forAll { (n: Int, a: Int, b: Int) => 9 | (HigherOrderSolution.nTimes(HigherOrder.plus, a, b, n) ?= HigherOrder.testNTimes(HigherOrder.plus, a, b, n)) && 10 | (HigherOrderSolution.nTimes(HigherOrder.multiply, a, b, n) ?= HigherOrder.testNTimes(HigherOrder.multiply, a, b, n)) 11 | } 12 | 13 | property("anonymous function") = forAll { (n: Int, a: Int, b: Int) => 14 | HigherOrderSolution.nTimes((x, y) => (if (x > y) x else y), a, b, n) ?= HigherOrder.testAnonymousNTimes(a, b, n) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/PatternMatchingSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | object PatternMatchingSolution { 4 | 5 | import PatternMatching._ 6 | 7 | def intToString(value: Int): String = value match { 8 | case 1 => "it is one" 9 | case 2 => "it is two" 10 | case 3 => "it is three" 11 | case _ => "what's that" 12 | } 13 | 14 | def isMaxOrMoritz(kid: String): Boolean = kid match { 15 | case "Max" | "max" => true 16 | case "Moritz" | "moritz" => true 17 | case _ => false 18 | } 19 | 20 | def isEven(value: Int): Boolean = value match { 21 | case _ if value % 2 == 0 => true 22 | case _ => false 23 | } 24 | 25 | def playerAWins(a: Hand, b: Hand): Result = (a, b) match { 26 | case (Rock, Scissor) => Win 27 | case (Scissor, Paper) => Win 28 | case (Paper, Rock) => Win 29 | case (ha, hb) if ha == hb => Draw 30 | case _ => Lose 31 | } 32 | 33 | def extractWeight(animal: Animal): Int = animal match { 34 | case Mammal(_, _, weight) => weight 35 | case _ => -1 36 | } 37 | 38 | def updateFood(animal: Animal): Animal = animal match { 39 | case Bird(name, _) => Bird(name, Plants) 40 | case Fish(name, _) => Fish(name, Plants) 41 | case _ => animal 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /exercise1_scala_101/src/test/scala/exercise1/PatternMatchingSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise1 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Gen 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Prop._ 7 | 8 | object PatternMatchingSpec extends Properties("pattern matching") { 9 | 10 | import PatternMatching._ 11 | 12 | property("Ints to Strings") = forAll(Gen.oneOf(1, 2, 3, 4)) { value => 13 | PatternMatchingSolution.intToString(value) ?= testIntToString(value) 14 | } 15 | 16 | property("is it Max or Moritz") = forAll(Gen.oneOf("Max", "max", "Moritz", "moritz", "Joe", "Jim", "mAx")) { value => 17 | PatternMatchingSolution.isMaxOrMoritz(value) ?= testIsMaxAndMoritz(value) 18 | } 19 | 20 | property("is a number even") = forAll { value: Int => 21 | PatternMatchingSolution.isEven(value) ?= testIsEven(value) 22 | } 23 | 24 | implicit val handGen = Arbitrary[Hand](Gen.oneOf(Rock, Paper, Scissor)) 25 | 26 | property("rock paper scissors, does player a win") = forAll { (ha: Hand, hb: Hand) => 27 | PatternMatchingSolution.playerAWins(ha, hb) ?= testWinsA(ha, hb) 28 | } 29 | 30 | implicit val animalGen: Arbitrary[Animal] = Arbitrary(for { 31 | name <- Gen.oneOf("cat", "parrot", "goldfish") 32 | weight <- Gen.posNum[Int] 33 | } yield name match { 34 | case "cat" => Mammal(name, Meat, weight) 35 | case "parrot" => Bird(name, Vegetables) 36 | case "goldfish" => Fish(name, Plants) 37 | }) 38 | 39 | property("extract mammal weight") = forAll { animal: Animal => 40 | PatternMatchingSolution.extractWeight(animal) ?= testExtractMammalWeight(animal) 41 | } 42 | 43 | property("update food") = forAll { animal: Animal => 44 | PatternMatchingSolution.updateFood(animal) ?= testUpdateFood(animal) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/main/scala/exercise2/Compositions.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | /** An Option indicates if an operation returned a result or not. This is often used when 4 | * searching for values or when operations can fail and you don't care about the 5 | * reason. 6 | * 7 | * The composition functions `map` and `flatMap` are context sensitive. If the Option 8 | * is a None they ignore any function application since no value is available. On the 9 | * other hand, if it is a Some the functions are applied to transform the value. 10 | */ 11 | sealed trait Option[A] { 12 | 13 | def map[B](f: A => B): Option[B] 14 | def flatMap[B](f: A => Option[B]): Option[B] 15 | } 16 | case class Some[A](a: A) extends Option[A] { 17 | 18 | def map[B](f: A => B): Option[B] = Some(f(a)) 19 | def flatMap[B](f: A => Option[B]): Option[B] = f(a) 20 | } 21 | case class None[A]() extends Option[A] { 22 | 23 | def map[B](f: A => B): Option[B] = None() 24 | def flatMap[B](f: A => Option[B]): Option[B] = None() 25 | } 26 | 27 | /** Implement your solutions within the test functions. */ 28 | object Compositions { 29 | 30 | // a) Compose the given functions. You can implement your solution directly in the test-function. 31 | // DO NOT change the signature 32 | 33 | def testCompose[A, B, C, D](f: A => B) 34 | (g: B => C) 35 | (h: C => D): A => D = ??? 36 | 37 | // b) Combose the functions using `map` and `flatMap`. You can implement your solution directly in the test-function. 38 | // DO NOT change the signature 39 | 40 | def testMapFlatMap[A, B, C, D](f: A => Option[B]) 41 | (g: B => Option[C]) 42 | (h: C => D): Option[A] => Option[D] = ??? 43 | 44 | // c) Combose the functions using for-comprehension. You can implement your solution directly in the test-function. 45 | // DO NOT change the signature 46 | 47 | def testForComprehension[A, B, C, D](f: A => Option[B]) 48 | (g: B => Option[C]) 49 | (h: C => D): Option[A] => Option[D] = ??? 50 | } 51 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/main/scala/exercise2/RecursiveData.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | sealed trait List[A] 4 | case class Cons[A](head: A, tail: List[A]) extends List[A] 5 | case class Nil[A]() extends List[A] 6 | 7 | /** Write your solutions as stand-alone functions. */ 8 | object RecursiveData { 9 | 10 | // a) Implement a function which determines if a `List[Int]` is empty or not. 11 | 12 | 13 | 14 | // apply your solution-function from (a) here, DO NOT change the signature 15 | def testListIntEmpty(list: List[Int]): Boolean = false 16 | 17 | // b) Implement a function which gets the head of a `List[Int]` or returns -1 if empty. 18 | 19 | 20 | 21 | // apply your solution-function from (b) here, DO NOT change the signature 22 | def testListIntHead(list: List[Int]): Int = 0 23 | 24 | // c) Can we change `List[A]` to guarantee to be not-empty? 25 | 26 | 27 | /* d) Implement a generic Tree which has its values in the leafs and consists of: 28 | * node - left and right Tree 29 | * leaf - a value of type A 30 | */ 31 | 32 | } 33 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/main/scala/exercise2/RecursiveFunctions.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | import scala.annotation.tailrec 4 | 5 | /** Implement stand-alone functions for your solutions. 6 | * REMARK: Try to make all functions tail recursive; use the annotation to proof it. 7 | * 8 | * A function is tail recursive if if: 9 | * 1. it is single direct recursion 10 | * 2. the last expression is the recursive call 11 | */ 12 | object RecursiveFunctions { 13 | 14 | def length[A](as: List[A]): Int = { 15 | @tailrec 16 | def loop(rem: List[A], agg: Int): Int = rem match { 17 | case Cons(_, tail) => loop(tail, agg + 1) 18 | case Nil() => agg 19 | } 20 | 21 | loop(as, 0) 22 | } 23 | 24 | /* a) Write a function that reverses a list: 25 | * def reverse[A](list: List[A]): List[A] 26 | */ 27 | 28 | 29 | 30 | // apply your solution-function from (a) here, DO NOT change the signature 31 | def testReverse[A](list: List[A]): List[A] = list 32 | 33 | /* b) Write a function that applies a function to every value of a list: 34 | * def map[A, B](list: List[A])(f: A => B): List[B] 35 | */ 36 | 37 | 38 | 39 | // apply your solution-function from (b) here, DO NOT change the signature 40 | def testMap[A, B](list: List[A], f: A => B): List[B] = Nil() 41 | 42 | /* c) Write a function that appends one list to another: 43 | * def append[A](l: List[A], r: List[A]): List[A] 44 | */ 45 | 46 | 47 | 48 | // apply your solution-function from (c) here, DO NOT change the signature 49 | def testAppend[A](l: List[A], r: List[A]): List[A] = l 50 | 51 | /* d) Write a function that applies a function to every value of a list: 52 | * def flatMap[A, B](list: List[A])(f: A => List[B]): List[B] 53 | * 54 | * it gets a function which creates a new List[B] for every element of type A in 55 | * the list. Therefore, you generate a List[List[B]]. You have to flatten this 56 | * structure. 57 | */ 58 | 59 | 60 | 61 | // apply your solution-function from (d) here, DO NOT change the signature 62 | def testFlatMap[A, B](list: List[A], f: A => List[B]): List[B] = Nil() 63 | 64 | /* e) Question: Is it possible to write a tail recursive map function for `Tree`s? If no, why and are you sure :) ? */ 65 | } 66 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/test/scala/exercise2/CompositionsSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | object CompositionsSolution { 4 | 5 | def compose[A, B, C, D](f: A => B) 6 | (g: B => C) 7 | (h: C => D): A => D = h.compose(g.compose(f)) 8 | 9 | def mapFlatMap[A, B, C, D](f: A => Option[B]) 10 | (g: B => Option[C]) 11 | (h: C => D): Option[A] => Option[D] = opt => { 12 | opt 13 | .flatMap(f) 14 | .flatMap(g) 15 | .map(h) 16 | } 17 | 18 | def forComprehension[A, B, C, D](f: A => Option[B]) 19 | (g: B => Option[C]) 20 | (h: C => D): Option[A] => Option[D] = opt => { 21 | for { 22 | v <- opt 23 | d <- f(v) 24 | s <- g(d) 25 | } yield h(s) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/test/scala/exercise2/CompositionsSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Arbitrary 5 | import org.scalacheck.Gen 6 | import org.scalacheck.Prop._ 7 | 8 | object CompositionsSpec extends Properties("compositions") { 9 | 10 | implicit val optionGen = Arbitrary[Option[Int]] { 11 | Gen.oneOf(true, false).flatMap { isSome => 12 | if (isSome) Gen.posNum[Int].map(r => Some(r)) 13 | else Gen.posNum[Int].map(_ => None()) 14 | } 15 | } 16 | 17 | def double(a: Int): Double = a * 2.0 18 | def show(d: Double): String = d.toString 19 | def prefix(s: String): String = "AWESOME " + s 20 | 21 | property("composition") = forAll { i: Int => 22 | Compositions.testCompose(double)(show)(prefix)(i) =? CompositionsSolution.compose(double)(show)(prefix)(i) 23 | } 24 | 25 | def doubleOpt(a: Int): Option[Double] = Some(a * 2.0) 26 | def showOpt(d: Double): Option[String] = Some(d.toString) 27 | 28 | property("map and flatMap") = forAll { opt: Option[Int] => 29 | Compositions.testMapFlatMap(doubleOpt)(showOpt)(prefix)(opt) =? CompositionsSolution.mapFlatMap(doubleOpt)(showOpt)(prefix)(opt) 30 | } 31 | 32 | property("for-comprehension") = forAll { opt: Option[Int] => 33 | Compositions.testForComprehension(doubleOpt)(showOpt)(prefix)(opt) =? CompositionsSolution.forComprehension(doubleOpt)(showOpt)(prefix)(opt) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/test/scala/exercise2/RecursiveDataSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | object RecursiveDataSolution { 4 | 5 | def isListEmpty(list: List[Int]): Boolean = list match { 6 | case Cons(_, _) => false 7 | case Nil() => true 8 | } 9 | 10 | def listHead(list: List[Int]): Int = list match { 11 | case Cons(head, _) => head 12 | case Nil() => -1 13 | } 14 | 15 | sealed trait Tree[A] 16 | case class Node[A](left: Tree[A], right: Tree[A]) extends Tree[A] 17 | case class Leaf[A](a: A) extends Tree[A] 18 | 19 | val tree = Node(Node(Leaf(0), Leaf(1)), Leaf(2)) 20 | } 21 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/test/scala/exercise2/RecursiveDataSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Gen 5 | import org.scalacheck.Prop._ 6 | 7 | object RecursiveDataSpec extends Properties("recursive data") { 8 | 9 | val listGen = Gen.choose(0, 10).flatMap { length => 10 | def generateList(acc: List[Int], l: Int): Gen[List[Int]] = { 11 | if (l == 0) 12 | acc 13 | else for { 14 | head <- Gen.posNum[Int] 15 | list <- generateList(Cons(head, acc), l - 1) 16 | } yield list 17 | } 18 | 19 | generateList(Nil(), length) 20 | } 21 | 22 | property("List[Int] head") = forAll(listGen) { list => 23 | RecursiveData.testListIntHead(list) =? RecursiveDataSolution.listHead(list) 24 | } 25 | 26 | property("List[Int] is empty") = forAll(listGen) { list => 27 | RecursiveData.testListIntEmpty(list) =? RecursiveDataSolution.isListEmpty(list) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/test/scala/exercise2/RecursiveFunctionsSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | import scala.annotation.tailrec 4 | 5 | object RecursiveFunctionsSolution { 6 | 7 | def reverse[A](list: List[A]): List[A] = { 8 | @tailrec 9 | def loop(rem: List[A], agg: List[A]): List[A] = rem match { 10 | case Nil() => agg 11 | case Cons(a, tail) => loop(tail, Cons(a, agg)) 12 | } 13 | 14 | loop(list, Nil()) 15 | } 16 | 17 | def map[A, B](list: List[A])(f: A => B): List[B] = { 18 | @tailrec 19 | def loop(rem: List[A], agg: List[B]): List[B] = rem match { 20 | case Nil() => agg 21 | case Cons(a, tail) => loop(tail, Cons(f(a), agg)) 22 | } 23 | 24 | reverse(loop(list, Nil())) 25 | } 26 | 27 | def append[A](l: List[A], r: List[A]): List[A] = { 28 | @tailrec 29 | def loop(lReversedRem: List[A], agg: List[A]): List[A] = lReversedRem match { 30 | case Nil() => agg 31 | case Cons(a, tail) => loop(tail, Cons(a, agg)) 32 | } 33 | 34 | loop(reverse(l), r) 35 | } 36 | 37 | def flatMap[A, B](list: List[A])(f: A => List[B]): List[B] = { 38 | @tailrec 39 | def loop(rem: List[A], agg: List[B]): List[B] = rem match { 40 | case Nil() => agg 41 | case Cons(a, tail) => loop(tail, append(f(a), agg)) 42 | } 43 | 44 | reverse(loop(list, Nil())) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /exercise2_fp_101/src/test/scala/exercise2/RecursiveFunctionsSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise2 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Gen 5 | import org.scalacheck.Arbitrary 6 | import org.scalacheck.Arbitrary.arbitrary 7 | import org.scalacheck.Prop._ 8 | 9 | import scala.annotation.tailrec 10 | 11 | object RecursiveFunctionsSpec extends Properties("recursive functions") { 12 | 13 | implicit val listGen = Arbitrary[List[Int]] { 14 | Gen.choose(0, 10).flatMap { length => 15 | def generateList(acc: List[Int], l: Int): Gen[List[Int]] = { 16 | if (l == 0) 17 | acc 18 | else for { 19 | head <- Gen.posNum[Int] 20 | list <- generateList(Cons(head, acc), l - 1) 21 | } yield list 22 | } 23 | 24 | generateList(Nil(), length) 25 | } 26 | } 27 | 28 | val replicationGen = for { 29 | n <- Gen.choose(0, 10) 30 | a <- arbitrary[String] 31 | } yield (n, a) 32 | 33 | property("reverse lists") = forAll { list: List[Int] => 34 | RecursiveFunctions.testReverse(list) =? RecursiveFunctionsSolution.reverse(list) 35 | } 36 | 37 | property("map values") = forAll { list: List[Int] => 38 | RecursiveFunctions.testMap[Int, String](list, _.toString) =? RecursiveFunctionsSolution.map(list)(_.toString) 39 | } 40 | 41 | property("append lists") = forAll { (l: List[Int], r: List[Int]) => 42 | RecursiveFunctions.testAppend(l, r) =? RecursiveFunctionsSolution.append(l, r) 43 | } 44 | 45 | property("flatMap values") = forAll { list: List[Int] => 46 | RecursiveFunctions.testFlatMap[Int, String](list, a => Cons(a.toString, Nil())) =? RecursiveFunctionsSolution.flatMap(list)(a => Cons(a.toString, Nil())) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/main/scala/exercise3/Adts.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import scala.util.{Try, Failure, Success} 4 | 5 | /** Implement stand-alone functions as solutions. 6 | * 7 | * List(1, 2) match { 8 | * case head :: tail => ??? 9 | * case Nil => ??? 10 | * case l => ??? 11 | * } 12 | * 13 | * Option(1) match { 14 | * case Some(a) => ??? 15 | * case None => ??? 16 | * } 17 | * 18 | * Either.cond(true, 1, "right") match { 19 | * case Left(i) => ??? 20 | * case Right(s) => ??? 21 | * } 22 | * 23 | * Try(impureExpression()) match { 24 | * case Success(a) => ??? 25 | * case Failure(error) => ??? 26 | * } 27 | * 28 | * Try(impureExpression()).toEither 29 | * 30 | */ 31 | object Adts { 32 | 33 | // a) Given a List[Int] return the nth element 34 | 35 | 36 | 37 | // apply your solution-function from (a) here, DO NOT change the signature 38 | def testGetNth(list: List[Int], n: Int): Option[Int] = None 39 | 40 | // b) Double the given number. 41 | 42 | 43 | 44 | // apply your solution-function from (b) here, DO NOT change the signature 45 | def testDouble(n: Option[Int]): Option[Int] = n 46 | 47 | // c) Check if a given Int is even. If so, return it as a Right. Otherwise, return Left("Not an even number."). 48 | 49 | 50 | 51 | // apply your solution-function from (c) here, DO NOT change the signature 52 | def testIsEven(n: Int): Either[String, Int] = Left("meh") 53 | 54 | // d) Safe division for Integers. Return Right with the result or Left("You cannot divide by zero."). 55 | 56 | 57 | 58 | // apply your solution-function from (d) here, DO NOT change the signature 59 | def testSafeDivide(a: Int, b: Int): Either[String, Int] = Left("meh") 60 | 61 | // e) Given an impure function handle its Exceptions and recover from them by returning 0. 62 | 63 | 64 | 65 | // apply your solution-function from (e) here, DO NOT change the signature 66 | def testGoodOldJava(impure: String => Int, str: String): Try[Int] = Failure(new IllegalArgumentException("meh")) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/main/scala/exercise3/Maps.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | /** Write your solution into the test functions. 4 | * 5 | * https://docs.scala-lang.org/overviews/collections/maps.html 6 | */ 7 | object Maps { 8 | 9 | case class User(name: String, age: Int) 10 | 11 | /* a) Given a Seq[User] group the users by name (`groupBy`) and calculate the average age: `name -> averageAge` 12 | * You can implement your solution directly in the test-function. DO NOT change the signature. 13 | */ 14 | def testGroupUsers(users: Seq[User]): Map[String, Int] = Map.empty 15 | 16 | /* b) Given a `Map[String, User]` from user name to `User`, how many Users with the substring "Baggins" are in the Map? 17 | * You can implement your solution directly in the test-function. DO NOT change the signature. 18 | */ 19 | def testNumberFrodos(map: Map[String, User]): Int = 0 20 | 21 | /* c) Remove all users under age 1000 (Wizards only :). 22 | * You can implement your solution directly in the test-function. DO NOT change the signature. 23 | */ 24 | def testUnderaged(map: Map[String, User]): Map[String, User] = map 25 | } 26 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/main/scala/exercise3/Sequence.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import scala.annotation.tailrec 4 | 5 | /** Write your solutions in the test functions. 6 | * 7 | * Seq(1, 2) match { 8 | * case head +: tail => ??? 9 | * case Nil => ??? 10 | * case s => ??? 11 | * } 12 | * 13 | * https://www.scala-lang.org/api/2.12.0/scala/collection/Seq.html 14 | */ 15 | // NOTE: write tail recursive functions 16 | // NOTE: try to use the methods you have seen so far 17 | object Sequence { 18 | 19 | /* a) Find the last element of a Seq. 20 | * You can implement your solution directly in the test-function. DO NOT change the signature. 21 | */ 22 | def testLastElement[A](seq: Seq[A]): Option[A] = ??? 23 | 24 | /* b) Zip two Seqs (e.g. Seq(1, 2) and Seq(3, 4) become Seq((1, 3), (2, 4))) - when one Seq is longer ignore the remaining elements. 25 | * You can implement your solution directly in the test-function. DO NOT change the signature. 26 | */ 27 | def testZip[A](a: Seq[A], b: Seq[A]): Seq[(A, A)] = ??? 28 | 29 | /* c) Check if a condition holds for all elements in Seq. 30 | * You can implement your solution directly in the test-function. DO NOT change the signature. 31 | */ 32 | def testForAll[A](seq: Seq[A])(cond: A => Boolean): Boolean = false 33 | 34 | /* d) Is Seq a palindrome, e.g. Seq(1, 2, 3, 2, 1)? 35 | * You can implement your solution directly in the test-function. DO NOT change the signature. 36 | */ 37 | def testPalindrom[A](seq: Seq[A]): Boolean = false 38 | 39 | /* e) Implement flatMap with foldLeft. 40 | * You can implement your solution directly in the test-function. DO NOT change the signature. 41 | */ 42 | def testFlatMap[A, B](seq: Seq[A])(f: A => Seq[B]): Seq[B] = ??? 43 | } 44 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/main/scala/exercise3/Strings.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | /** Write your solutions in the test functions. 4 | * 5 | * https://www.scala-lang.org/api/2.12.3/scala/collection/immutable/StringOps.html 6 | */ 7 | object Strings { 8 | 9 | /* a) Map all Chars into upper case (don't use capitalize). 10 | * You can implement your solution directly in the test-function. DO NOT change the signature. 11 | */ 12 | def testUppercase(str: String): String = "" 13 | 14 | /* b) Interpolate the following values in the String: 15 | * Hi my name is and I am years old. 16 | * You can implement your solution directly in the test-function. DO NOT change the signature. 17 | */ 18 | def testInterpolations(name: String, age: Int): String = "" 19 | 20 | /* c) Add two numbers as interpolation in the following multiline String: 21 | * Hi, 22 | * now follows a quite hard calculation. We try to add: 23 | * a := 24 | * b := 25 | * 26 | * result is 27 | * 28 | * You can implement your solution directly in the test-function. DO NOT change the signature. 29 | */ 30 | def testComputation(a: Int, b: Int): String = "" 31 | 32 | /* d) Has the String length two? If so return the first two Chars as String otherwise the whole String. 33 | * You can implement your solution directly in the test-function. DO NOT change the signature. 34 | */ 35 | def testTakeTwo(str: String): String = "" 36 | } 37 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/AdtsSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import scala.util.Try 4 | 5 | object AdtsSolution { 6 | 7 | def getNth(list: List[Int], n: Int): Option[Int] = list.drop(n).headOption 8 | 9 | def double(n: Option[Int]): Option[Int] = n.map(_ * 2) 10 | 11 | def isEven(n: Int): Either[String, Int] = 12 | if (n % 2 == 0) Right(n) 13 | else Left("Not an even number.") 14 | 15 | def safeDivide(a: Int, b: Int): Either[String, Int] = 16 | if (b == 0) Left("You cannot divide by zero.") 17 | else Right(a / b) 18 | 19 | def goodOldJava(impure: String => Int, str: String): Try[Int] = 20 | Try(impure(str)).recover { 21 | case _ => 0 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/AdtsSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.{Arbitrary, Gen} 5 | import org.scalacheck.Prop._ 6 | 7 | object AdtsSpec extends Properties("ADT") { 8 | 9 | property("get nth element") = forAll(listGen) { case (list, n) => 10 | Adts.testGetNth(list, n) =? AdtsSolution.getNth(list, n) 11 | } 12 | 13 | property("double optional number") = forAll { n: Option[Int] => 14 | Adts.testDouble(n) =? AdtsSolution.double(n) 15 | } 16 | 17 | property("is an even number") = forAll { n: Int => 18 | Adts.testIsEven(n) =? AdtsSolution.isEven(n) 19 | } 20 | 21 | property("safe divide") = forAll { (a: Int, b: Int) => 22 | Adts.testSafeDivide(a, b) =? AdtsSolution.safeDivide(a, b) 23 | } 24 | 25 | property("good old unsafe code") = forAll { str: String => 26 | def javaStyle(str: String): Int = { 27 | if (str.length > 20) throw new IllegalArgumentException("nope") 28 | else str.length 29 | } 30 | 31 | Adts.testGoodOldJava(javaStyle, str) =? AdtsSolution.goodOldJava(javaStyle, str) 32 | } 33 | 34 | val listGen = for { 35 | list <- Gen.listOfN(10, Gen.choose(0, 1000)) 36 | n <- Gen.choose(0, 100) 37 | nTooLarge <- Gen.oneOf(true, false) 38 | } yield { 39 | if (nTooLarge) 40 | (list, list.length + n) 41 | else if (n >= list.length) 42 | (list, list.length - 1) 43 | else 44 | (list, n) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/MapsSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | object MapsSolution { 4 | 5 | import Maps._ 6 | 7 | def averageAge(users: Seq[User]): Map[String, Int] = 8 | users 9 | .groupBy(_.name) 10 | .mapValues(s => s.map(_.age).sum / s.length) 11 | 12 | def numberOfFrodos(users: Map[String, User]): Int = users.count(_._1.contains("Baggins")) 13 | 14 | def underaged(users: Map[String, User]): Map[String, User] = users.filter(_._2.age >= 1000) 15 | } 16 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/MapsSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.{Arbitrary, Gen} 5 | import org.scalacheck.Prop._ 6 | 7 | import scala.annotation.tailrec 8 | 9 | object MapsSpec extends Properties("Map") { 10 | 11 | import Maps._ 12 | 13 | val userGen = for { 14 | age <- Gen.choose(0, 2019) 15 | name <- Gen.oneOf("Gandalf", "Frodo Baggins", "Saroman", "Bilbo Baggins", "Gimli", "Elessar") 16 | } yield User(name, age) 17 | 18 | implicit val userArb = Arbitrary[User](userGen) 19 | 20 | property("groupd users") = forAll { users: Seq[User] => 21 | testGroupUsers(users) =? MapsSolution.averageAge(users) 22 | } 23 | 24 | property("number of Frodos") = forAll(userMapGen) { users => 25 | testNumberFrodos(users) =? MapsSolution.numberOfFrodos(users) 26 | } 27 | 28 | property("underaged") = forAll(userMapGen) { users => 29 | testUnderaged(users) =? MapsSolution.underaged(users) 30 | } 31 | 32 | val userMapGen = for { 33 | users <- Gen.sequence[List[User], User](List(userGen)) 34 | } yield users.groupBy(_.name).mapValues(_.head) 35 | } 36 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/SequenceSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import scala.annotation.tailrec 4 | 5 | object SequenceSolution { 6 | 7 | @tailrec 8 | def lastElement[A](seq: Seq[A]): Option[A] = seq match { 9 | case last +: Nil => Some(last) 10 | case _ +: tail => lastElement(tail) 11 | case Nil => None 12 | } 13 | 14 | def reverse[A](seq: Seq[A]): Seq[A] = seq.foldLeft(Seq.empty[A])((agg, a) => a +: agg) 15 | 16 | @tailrec 17 | def zip[A](a: Seq[A], b: Seq[A], agg: Seq[(A, A)] = Nil): Seq[(A, A)] = (a, b) match { 18 | case (ah +: at, bh +: bt) => zip(at, bt, (ah, bh) +: agg) 19 | case (Nil, _) | (_, Nil) => reverse(agg) 20 | } 21 | 22 | def forAllElements[A](seq: Seq[A])(cond: A => Boolean): Boolean = seq.foldLeft(true)((agg, a) => agg && cond(a)) 23 | 24 | def palindrom[A](seq: Seq[A]): Boolean = { 25 | val middle = seq.length / 2 + 1 26 | 27 | if (seq.isEmpty) 28 | true 29 | else 30 | forAllElements(zip(seq.take(middle), reverse(seq).take(middle)))(pair => pair._1 == pair._2) 31 | } 32 | 33 | def deduplicate[A](seq: Seq[A]): Seq[A] = reverse(seq.foldLeft(Seq.empty[A]) { (agg, a) => 34 | if (agg.contains(a)) agg 35 | else a +: agg 36 | }) 37 | 38 | def flatMap[A, B](seq: Seq[A])(f: A => Seq[B]): Seq[B] = seq.foldLeft(Seq.empty[B]) { (agg, a) => 39 | val bs = f(a) 40 | 41 | agg ++ bs 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/SequenceSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Gen 5 | import org.scalacheck.Prop._ 6 | 7 | object SequenceSpec extends Properties("sequence") { 8 | 9 | property("last element") = forAll { seq: Seq[Int] => 10 | Sequence.testLastElement(seq) =? SequenceSolution.lastElement(seq) 11 | } 12 | 13 | property("zip") = forAll { (a: Seq[Int], b: Seq[Int]) => 14 | Sequence.testZip(a, b) =? SequenceSolution.zip(a, b) 15 | } 16 | 17 | property("for all") = forAll { seq: Seq[Int] => 18 | Sequence.testForAll(seq)(_ > 3) =? SequenceSolution.forAllElements(seq)(_ > 3) 19 | } 20 | 21 | property("palindrom") = forAll(palindromeGen) { seq => 22 | Sequence.testPalindrom(seq) =? SequenceSolution.palindrom(seq) 23 | } 24 | 25 | property("flatMap") = forAll { seq: Seq[Int] => 26 | Sequence.testFlatMap(seq)(a => Seq(a, a + 1, a + 2)) =? SequenceSolution.flatMap(seq)(a => Seq(a, a + 1, a + 2)) 27 | } 28 | 29 | val palindromeGen = for { 30 | isPalindrom <- Gen.oneOf(true, false) 31 | seq <- Gen.sequence[Seq[Int], Int](List(Gen.posNum[Int])) 32 | } yield { 33 | if (isPalindrom) { 34 | val half = seq.take(seq.length / 2) 35 | 36 | half ++ half.reverse 37 | } 38 | else 39 | seq 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/StringsSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | object StringsSolution { 4 | 5 | def uppercase(str: String): String = str.map(_.toUpper) 6 | 7 | def interpolations(name: String, age: Int) = s"Hi my name is $name and I am $age years old." 8 | 9 | def computation(a: Int, b: Int): String = 10 | s""" 11 | |Hi, 12 | |now follows a quite hard calculation. We try to add: 13 | | a := $a 14 | | b := $b 15 | | 16 | | result is ${a + b} 17 | """.stripMargin 18 | 19 | def takeTwo(str: String): String = 20 | if (str.length >= 2) str.take(2) 21 | else str 22 | } 23 | -------------------------------------------------------------------------------- /exercise3_std_lib/src/test/scala/exercise3/StringsSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise3 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Gen 5 | import org.scalacheck.Prop._ 6 | 7 | object StringsSpec extends Properties("String") { 8 | 9 | property("uppercase") = forAll { str: String => 10 | Strings.testUppercase(str) =? StringsSolution.uppercase(str) 11 | } 12 | 13 | property("interpolation 1") = forAll { (name: String, age: Int) => 14 | Strings.testInterpolations(name, age) =? StringsSolution.interpolations(name, age) 15 | } 16 | 17 | property("interpolation 2 (computation)") = forAll { (a: Int, b: Int) => 18 | Strings.testComputation(a, b) =? StringsSolution.computation(a, b) 19 | } 20 | 21 | property("take two") = forAll { str: String => 22 | Strings.testTakeTwo(str) =? StringsSolution.takeTwo(str) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercise4_typeclasses_101/src/main/scala/exercise4/Typeclasses.scala: -------------------------------------------------------------------------------- 1 | package exercise4 2 | 3 | object Typeclasses { 4 | 5 | // a) Define a type class Reversable which reverses values. 6 | 7 | trait Reversable[T] 8 | 9 | object Reversable { 10 | def reverse[T: Reversable](a: T): T = ??? 11 | } 12 | 13 | // b) Provide an instance of Reverse for String. 14 | 15 | 16 | 17 | // apply your solution-typeclass from (a) here, DO NOT change the signature 18 | def testReversableString(str: String): String = str 19 | 20 | // c) Define a type class Smash so that it provides a function to smash values of the same type together. 21 | 22 | trait Smash[T] 23 | 24 | object Smash { 25 | def smash[T : Smash](a: T, b: T): T = ??? 26 | } 27 | 28 | // d) Provide an instance for Smash for the Int and Double types. 29 | // Use addition for the Int type and multiplication for the Double type. 30 | 31 | 32 | 33 | // apply your solution-typeclass from (d) here, DO NOT change the signature 34 | def testSmashInt(a: Int, b: Int): Int = a 35 | 36 | // apply your solution-typeclass from (d) here, DO NOT change the signature 37 | def testSmashDouble(a: Double, b: Double): Double = a 38 | 39 | 40 | // e) Provide an instance for Smash for String type such that it concatenates the input. 41 | 42 | 43 | 44 | // apply your solution-typeclass from (e) here, DO NOT change the signature 45 | def testSmashString(a: String, b: String): String = a 46 | } 47 | -------------------------------------------------------------------------------- /exercise4_typeclasses_101/src/test/scala/exercise4/TypeclassSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise4 2 | 3 | object TypeclassSolution { 4 | 5 | trait Reversable[A] { 6 | 7 | def reverse(a: A): A 8 | } 9 | 10 | object Reversable { 11 | 12 | implicit val stringReverse = new Reversable[String] { 13 | override def reverse(a: String): String = a.reverse 14 | } 15 | 16 | def reverse[A: Reversable](a: A): A = implicitly[Reversable[A]].reverse(a) 17 | } 18 | 19 | 20 | trait Smash[A] { 21 | 22 | def smash(a: A, b: A): A 23 | } 24 | 25 | object Smash { 26 | implicit val smashInt = new Smash[Int] { 27 | override def smash(a: Int, b: Int): Int = a + b 28 | } 29 | 30 | implicit val smashDouble = new Smash[Double] { 31 | override def smash(a: Double, b: Double): Double = a * b 32 | } 33 | 34 | implicit val smashString = new Smash[String] { 35 | override def smash(a: String, b: String): String = a ++ b 36 | } 37 | 38 | implicit val smashList = new Smash[List[_]] { 39 | override def smash(a: List[_], b: List[_]): List[_] = a ++ b 40 | } 41 | 42 | def smash[A : Smash](a: A, b: A): A = implicitly[Smash[A]].smash(a, b) 43 | } 44 | 45 | 46 | def testReversableString(str: String) = Reversable.reverse(str) 47 | def testSmashInt(a: Int, b: Int): Int = Smash.smash(a, b) 48 | def testSmashDouble(a: Double, b: Double): Double = Smash.smash(a, b) 49 | def testSmashString(a: String, b: String): String = Smash.smash(a, b) 50 | } 51 | -------------------------------------------------------------------------------- /exercise4_typeclasses_101/src/test/scala/exercise4/TypeclassSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise4 2 | 3 | import org.scalacheck.Prop._ 4 | import org.scalacheck.{Gen, Properties} 5 | 6 | object TypeclassSpec extends Properties("Map") { 7 | property("string reverse") = forAll { string: String => 8 | TypeclassSolution.testReversableString(string) ?= Typeclasses.testReversableString(string) 9 | } 10 | 11 | property("smash int") = forAll { inp: (Int, Int) => 12 | TypeclassSolution.testSmashInt(inp._1, inp._2) ?= Typeclasses.testSmashInt(inp._1, inp._2) 13 | } 14 | 15 | property("smash double") = forAll { inp: (Double, Double) => 16 | TypeclassSolution.testSmashDouble(inp._1, inp._2) ?= Typeclasses.testSmashDouble(inp._1, inp._2) 17 | } 18 | 19 | property("smash string") = forAll { inp: (String, String) => 20 | TypeclassSolution.testSmashString(inp._1, inp._2) ?= Typeclasses.testSmashString(inp._1, inp._2) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/main/scala/exercise5/Core.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import cats._ 4 | 5 | /** The following code snippet show you which type-class functions you have to implement: 6 | * 7 | * new Functor[ErrorOr] { 8 | * def map[A, B](fa: ErrorOr[A])(f: A => B): ErrorOr[B] 9 | * } 10 | * 11 | * 12 | * new Applicative[ErrorOr] { 13 | * def pure[A](a: A): ErrorOr[A] = ??? 14 | * 15 | * def ap[A, B](ff: ErrorOr[A => B])(fa: ErrorOr[A]): ErrorOr[B] = ??? 16 | * } 17 | * 18 | * new Monad[ErrorOr] { 19 | * def pure[A](a: A): ErrorOr[A] = ??? 20 | * 21 | * def flatMap[A, B](fa: ErrorOr[A])(f: A => ErrorOr[B]): ErrorOr[B] = ??? 22 | * 23 | * // here you have to reduce `f(a)` to `ErrorOr[B]` by ignoring `Left` values 24 | * def tailRecM[A, B](a: A)(f: A => ErrorOr[Either[A,B]]): ErrorOr[B] = ??? 25 | * } 26 | */ 27 | object Core { 28 | 29 | type ErrorOr[A] = Either[String, A] 30 | 31 | // a) Implement implicit Functor[ErrorOr] instance. 32 | 33 | 34 | 35 | 36 | // apply your solution-typeclass from (a) here using cats syntax if possible, DO NOT change the signature 37 | def testErrorOrFunctor[A, B](fa: ErrorOr[A], f: A => B): ErrorOr[B] = Left("wrong") 38 | 39 | // b) Implement an implicit Applicative[ErrorOr] instance. 40 | 41 | 42 | 43 | 44 | // apply your solution-typeclass from (b) here using cats syntax if possible, DO NOT change the signature 45 | def testErrorOrApplicative[A, B](ff: ErrorOr[A => B], fa: ErrorOr[A]): ErrorOr[B] = Left("wrong") 46 | 47 | // c) Apply all parameters to the function. 48 | 49 | // apply your solution-typeclass from (c) here using cats syntax if possible, DO NOT change the signature 50 | def testApplyApp(f: Int => Int => String, x: ErrorOr[Int], y: ErrorOr[Int]): ErrorOr[String] = { 51 | import cats.syntax.all._ 52 | 53 | Left("wrong") 54 | } 55 | 56 | // d) Implement an implicit Monad[ErrorOr] instance. 57 | 58 | 59 | 60 | 61 | def testErrorOrMonad[A, B](fa: ErrorOr[A], f: A => ErrorOr[B]): ErrorOr[B] = Left("wrong") 62 | 63 | } 64 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/main/scala/exercise5/Kernel.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import cats.kernel._ 4 | import cats.implicits._ 5 | import Comparison._ 6 | 7 | /** Short type class summary: 8 | * 9 | * trait Eq[A] { 10 | * def eqv(x: A, y: A): Boolean = ??? 11 | * } 12 | * 13 | * trait Order[A] { 14 | * def compare(x: A, y: A): Int = ??? 15 | * } 16 | * 17 | * trait Semigroup[A] { 18 | * def combine(x: A, y: A): A = ??? 19 | * } 20 | */ 21 | object Kernel { 22 | 23 | // a) Test equality using Eq[_]. 24 | 25 | // apply your solution-typeclass from (a) here using cats syntax if possible, DO NOT change the signature 26 | def testEqString(x: String, y: String): Boolean = false 27 | 28 | // b) Test order using Order[_]. 29 | 30 | // apply your solution-typeclass from (b) here using cats syntax if possible, DO NOT change the signature 31 | def testCompareInt(x: Int, y: Int): Comparison = GreaterThan 32 | 33 | // c) Combine two list using Semigroup[_]. 34 | 35 | // apply your solution-typeclass from (c) here using cats syntax if possible, DO NOT change the signature 36 | def testCombineLists(x: List[Int], y: List[Int]): List[Int] = x 37 | 38 | 39 | // e) Can you find a type class in kernel which adds an empty element to Semigroup? How is it called? 40 | } 41 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/main/scala/exercise5/MonadTransformers.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import cats.instances.all.catsStdInstancesForList 4 | 5 | /** Implement a Monad instance for ErrorOrT[_], A]. Use the kind-projector `?` when writing `new Monad[ErrorOrT[F, ?]]`. 6 | */ 7 | 8 | object MonadTransformers { 9 | 10 | type ErrorOr[A] = Either[String, A] 11 | type ErrorOrT[F[_], A] = F[ErrorOr[A]] 12 | 13 | // apply your solution-typeclass from (a) here using cats syntax if possible, DO NOT change the signature 14 | def testOptionTransformer[A, B](fa: ErrorOrT[Option, A], f: A => ErrorOrT[Option, B]): ErrorOrT[Option, B] = None 15 | } 16 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/test/scala/exercise5/CoreSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import cats._ 4 | 5 | import scala.annotation.tailrec 6 | 7 | object CoreSolution { 8 | 9 | import Core.ErrorOr 10 | 11 | implicit val listFunct = new Functor[ErrorOr] { 12 | 13 | def map[A, B](fa: ErrorOr[A])(f: A => B): ErrorOr[B] = fa match { 14 | case Right(a) => Right(f(a)) 15 | case Left(e) => Left(e) 16 | } 17 | } 18 | 19 | def errorOrFunctor[A, B](fa: ErrorOr[A], f: A => B): ErrorOr[B] = Functor[ErrorOr].map(fa)(f) 20 | 21 | 22 | implicit val errorOrApp = new Applicative[ErrorOr] { 23 | 24 | def pure[A](a: A): ErrorOr[A] = Right(a) 25 | 26 | def ap[A, B](ff: ErrorOr[A => B])(fa: ErrorOr[A]): ErrorOr[B] = 27 | for { 28 | a <- fa 29 | f <- ff 30 | } yield f(a) 31 | } 32 | 33 | def errorOrApplicative[A, B](ff: ErrorOr[A => B], fa: ErrorOr[A]): ErrorOr[B] = 34 | Applicative[ErrorOr].ap(ff)(fa) 35 | 36 | def applyApp(f: Int => Int => String, x: ErrorOr[Int], y: ErrorOr[Int]): ErrorOr[String] = { 37 | import cats.syntax.all._ 38 | 39 | Applicative[ErrorOr].pure(f) <*> x <*> y 40 | } 41 | 42 | 43 | implicit val errorOrMon = new Monad[ErrorOr] { 44 | 45 | def pure[A](a: A): ErrorOr[A] = Right(a) 46 | 47 | def flatMap[A, B](fa: ErrorOr[A])(f: A => ErrorOr[B]): ErrorOr[B] = fa match { 48 | case Right(a) => f(a) 49 | case Left(e) => Left(e) 50 | } 51 | 52 | @tailrec 53 | def tailRecM[A, B](a: A)(f: A => ErrorOr[Either[A,B]]): ErrorOr[B] = f(a) match { 54 | case Right(Right(b)) => Right(b) 55 | case Right(Left(aa)) => tailRecM(aa)(f) 56 | case Left(e) => Left(e) 57 | } 58 | } 59 | 60 | def errorOrMonad[A, B](fa: ErrorOr[A], f: A => ErrorOr[B]): ErrorOr[B] = Monad[ErrorOr].flatMap(fa)(f) 61 | } 62 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/test/scala/exercise5/CoreSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | 6 | object CoreSpec extends Properties("Core") { 7 | 8 | import Core.ErrorOr 9 | 10 | property("ErrorOr Functor") = forAll { fa: ErrorOr[Int] => 11 | val f: Int => Int = _ * 1 12 | 13 | CoreSolution.errorOrFunctor(fa, f) ?= Core.testErrorOrFunctor(fa, f) 14 | } 15 | 16 | property("ErrorOr Applicative") = forAll { fa: ErrorOr[Int] => 17 | val ff: ErrorOr[Int => Int] = Right(_ * 2) 18 | 19 | CoreSolution.errorOrApplicative(ff, fa) ?= Core.testErrorOrApplicative(ff, fa) 20 | } 21 | 22 | property("apply Applicative") = forAll { (x: ErrorOr[Int], y: ErrorOr[Int]) => 23 | val f: Int => Int => String = a => b => (a * b).toString 24 | 25 | CoreSolution.applyApp(f, x, y) ?= Core.testApplyApp(f, x, y) 26 | } 27 | 28 | property("ErrorOr Monad") = forAll { fa: ErrorOr[Int] => 29 | val f: Int => ErrorOr[Int] = a => Right(a * 2) 30 | 31 | CoreSolution.errorOrMonad(fa, f) ?= Core.testErrorOrMonad(fa, f) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/test/scala/exercise5/KernelSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import cats.kernel._ 4 | import cats.implicits._ 5 | import Comparison._ 6 | 7 | object KernelSolution { 8 | 9 | def eqString(x: String, y: String): Boolean = x === y 10 | 11 | def compareInt(x: Int, y: Int): Comparison = Order[Int].comparison(x, y) 12 | 13 | def combineLists(x: List[Int], y: List[Int]): List[Int] = x |+| y 14 | } 15 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/test/scala/exercise5/KernelSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | 6 | object KernelSpec extends Properties("kernel") { 7 | 8 | property("String equality") = forAll { (x: String, y: String, eq: Boolean) => 9 | if (eq) 10 | KernelSolution.eqString(x, x) ?= Kernel.testEqString(x, x) 11 | else 12 | KernelSolution.eqString(x, y) ?= Kernel.testEqString(x, y) 13 | } 14 | 15 | property("Int comparison") = forAll { (x: Int, y: Int, eq: Boolean) => 16 | if (eq) 17 | KernelSolution.compareInt(x, x) ?= Kernel.testCompareInt(x, x) 18 | else 19 | KernelSolution.compareInt(x, y) ?= Kernel.testCompareInt(x, y) 20 | } 21 | 22 | property("List combination") = forAll { (x: List[Int], y: List[Int]) => 23 | KernelSolution.combineLists(x, y) ?= Kernel.testCombineLists(x, y) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/test/scala/exercise5/MonadTransformersSolution.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import cats.Monad 4 | import cats.instances.all.catsStdInstancesForOption 5 | 6 | object MonadTransformersSolution { 7 | 8 | import MonadTransformers.{ErrorOrT, ErrorOr} 9 | 10 | implicit def errorOrT[F[_]: Monad] = new Monad[ErrorOrT[F, ?]] { 11 | 12 | def pure[A](a: A): ErrorOrT[F, A] = Monad[F].pure(Right(a)) 13 | 14 | def flatMap[A, B](fa: ErrorOrT[F, A])(f: A => ErrorOrT[F, B]): ErrorOrT[F, B] = 15 | Monad[F].flatMap(fa) { 16 | case Right(a) => f(a) 17 | case Left(e) => Monad[F].pure(Left(e)) 18 | } 19 | 20 | // TODO: make this tailrec or at least stack-safe 21 | def tailRecM[A, B](a: A)(f: A => ErrorOrT[F, Either[A, B]]): ErrorOrT[F, B] = Monad[F].flatMap(f(a)) { 22 | case Right(Right(b)) => Monad[F].pure(Right(b)) 23 | case Right(Left(aa)) => tailRecM(aa)(f) 24 | case Left(e) => Monad[F].pure(Left(e)) 25 | } 26 | } 27 | 28 | def optionTransformer[A, B](fa: ErrorOrT[Option, A], f: A => ErrorOrT[Option, B]): ErrorOrT[Option, B] = 29 | Monad[ErrorOrT[Option, ?]].flatMap(fa)(f) 30 | } 31 | -------------------------------------------------------------------------------- /exercise5_typeclasses_incarnations/src/test/scala/exercise5/MonadTransformersSpec.scala: -------------------------------------------------------------------------------- 1 | package exercise5 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | 6 | object MonadTransformerssSpec extends Properties("Monad Transformer") { 7 | 8 | import MonadTransformers.ErrorOrT 9 | 10 | property("Option[ErrorOf] Monad") = forAll { fa: ErrorOrT[Option, Int] => 11 | val f: Int => ErrorOrT[Option, Int] = a => Some(Right(a * 2)) 12 | 13 | MonadTransformersSolution.optionTransformer(fa, f) ?= MonadTransformers.testOptionTransformer(fa, f) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /exercise7_io/src/main/scala/App.scala: -------------------------------------------------------------------------------- 1 | 2 | import cats.effect.{IO, IOApp} 3 | 4 | import scala.util.Try 5 | import scala.io.{Source, StdIn} 6 | 7 | import java.io.BufferedReader 8 | 9 | /** 10 | * This app is an cli tool reading a user database (file) from disk which stores user entries as follows: 11 | * 12 | * // name, age 13 | * Gandalf,2019 14 | * Frodo,31 15 | * ... 16 | * 17 | * The path to this file is given during application start: 18 | * 19 | * sbt> project io-exercises 20 | * sbt> run "/Users/paul.heymann/Private/lectures/exercise7_io/user.db" 21 | * 22 | * After reading and parsing the file the app is waiting for the user to search for a name in the database. 23 | * All results are printed to the console in the end. 24 | * 25 | * You can exit the program by tipping 'q'. 26 | */ 27 | 28 | /** 29 | * These IO methods might come in handy: 30 | * IO.pure[A](a: A): IO[A] 31 | * 32 | * IO(...).attempt: Either[Throwable, A] 33 | * 34 | * IO(...).bracket[B](use: A => IO[B])(release: A => IO[Unit]): IO[B] 35 | * 36 | * IO(...).unsafeRunSunc or use >>> IOApp <<< 37 | */ 38 | object App { 39 | 40 | case class User(name: String, age: Int) 41 | 42 | sealed trait Error 43 | case class ParseError(msg: String) extends Error 44 | 45 | /** Opens a file and hands us a reader which acts like a reference to the file. 46 | * 47 | * @param path path to the user db file 48 | * @return reference to the db file 49 | */ 50 | def readDbFile(path: String): BufferedReader = { 51 | new BufferedReader(Source.fromFile(path).reader) 52 | } 53 | 54 | /** Transforms a raw line to a [[User]] class. 55 | * 56 | * @param line user encoded as [[String]] 57 | * @return [[Right]] providing a [[User]] if the [[String]] is valid, else [[Left]] with [[ParseError]] 58 | */ 59 | def parse(line: String): Either[Error, User] = { 60 | line.split(',') match { 61 | case Array(name, ageStr) => Try(ageStr.toInt).fold[Either[Error, User]]( 62 | _ => Left(ParseError(s"not a number: $ageStr")), 63 | age => Right(User(name, age)) 64 | ) 65 | 66 | case _ => Left(ParseError(s"invalid user line format: $line")) 67 | } 68 | } 69 | 70 | /** This functions composes [[readDbFile]] and [[parse]] to transform all user entries to a 71 | * [[List[User]]]. 72 | * 73 | * @param path path to the user db file 74 | * @return [[Right]] of [[List[User]] if the whole file is valid, else [[Left]] with [[ParseError]] 75 | */ 76 | def loadDb(path: String): Either[Error, List[User]] = { 77 | val buffer = readDbFile(path) 78 | 79 | def loop(agg: List[User]): Either[Error, List[User]] = { 80 | val line = buffer.readLine() 81 | 82 | // buffer.readLine() return `null` if new line is left in the file 83 | if (line != null) parse(line).flatMap(user => loop(user :: agg)) 84 | else Right(agg.reverse) 85 | } 86 | 87 | loop(Nil) 88 | } 89 | 90 | def showResult(users: List[User]): String = { 91 | users.map(user => user.name + "," + user.age).mkString("----\n", "\n", "\n----\n") 92 | } 93 | 94 | def main(args: Array[String]): Unit = { 95 | if (args.length != 1) { 96 | // stop application if do not provide a user db path 97 | System.exit(1) 98 | } 99 | else { 100 | val dbPath = args(0) 101 | 102 | loadDb(dbPath) match { 103 | case Right(users) => 104 | // ask the user for input forever 105 | while(true) { 106 | val name = StdIn.readLine("Search name or exit (q): ") 107 | 108 | // stop application if the user inputs 'q' 109 | if (name == "q") System.exit(0) 110 | 111 | val result = users.filter(_.name.toLowerCase == name.toLowerCase) 112 | 113 | println(showResult(result)) 114 | } 115 | 116 | case Left(error) => 117 | println(s"ERROR: $error") 118 | } 119 | 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /exercise7_io/user.db: -------------------------------------------------------------------------------- 1 | Gandalf,2019 2 | Frodo,33 3 | Gimli,139 4 | Aragorn,87 5 | Bilbo,60 -------------------------------------------------------------------------------- /introduction/img/christianstein.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/introduction/img/christianstein.jpg -------------------------------------------------------------------------------- /introduction/img/davidkrentzlin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/introduction/img/davidkrentzlin.jpg -------------------------------------------------------------------------------- /introduction/img/floriansachse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/introduction/img/floriansachse.png -------------------------------------------------------------------------------- /introduction/img/paulheymann.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/introduction/img/paulheymann.jpg -------------------------------------------------------------------------------- /introduction/img/sbt_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 21 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /introduction/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scala Summer School 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /introduction/src/main/scala/Introduction.scala: -------------------------------------------------------------------------------- 1 | 2 | import PresentationUtil._ 3 | import japgolly.scalajs.react.ScalaComponent 4 | import japgolly.scalajs.react.vdom.html_<^._ 5 | import org.scalajs.dom 6 | 7 | import scala.scalajs.js.JSApp 8 | import scala.scalajs.js.annotation.JSExport 9 | 10 | object Introduction extends JSApp { 11 | 12 | import Enumeration._ 13 | 14 | val introduction = chapter( 15 | chapterSlide( 16 | <.h1("Scala Summer School") 17 | ), 18 | 19 | noHeaderSlide( 20 | <.h2("Welcome"), 21 | <.br, 22 | <.h3("And thanks for being here") 23 | ), 24 | 25 | noHeaderSlide( 26 | <.img( 27 | ^.alt := "Scala + Fp = Love", 28 | ^.src := "./img/scala_fp.svg" 29 | ) 30 | ), 31 | 32 | noHeaderSlide( 33 | <.h2("The Team") 34 | ), 35 | 36 | slide( 37 | "The Team", 38 | <.img( 39 | ^.cls := "team-img", 40 | ^.alt := "Paul Heymann", 41 | ^.src := "./img/paulheymann.jpg" 42 | ), 43 | <.p("Paul Heymann / Data Engineer @ XING") 44 | ), 45 | 46 | slide( 47 | "The Team", 48 | <.img( 49 | ^.cls := "team-img", 50 | ^.alt := "David Krentzlin", 51 | ^.src := "./img/davidkrentzlin.jpg" 52 | ), 53 | <.p("David Krentzlin / Software Engineer @ XING") 54 | ), 55 | 56 | slide( 57 | "The Team", 58 | <.img( 59 | ^.cls := "team-img", 60 | ^.alt := "Christian Stein", 61 | ^.src := "./img/christianstein.jpg" 62 | ), 63 | <.p("Christian Stein / Data Engineer @ XING") 64 | ), 65 | 66 | slide( 67 | "The Team", 68 | <.img( 69 | ^.cls := "team-img florian-img", 70 | ^.alt := "Florian Sachse", 71 | ^.src := "./img/floriansachse.png" 72 | ), 73 | <.p("Florian Sachse / Data Engineer @ XING") 74 | ), 75 | 76 | slide( 77 | "Lectures", 78 | Enumeration( 79 | Item.stable("Scala 101"), 80 | Item.fadeIn("Functional Programming 101"), 81 | Item.fadeIn("Standard Library"), 82 | Item.fadeIn("Small workshop project"), 83 | Item.fadeIn("Type Classes and Incarnations"), 84 | Item.fadeIn("Side Effect and IO"), 85 | Item.fadeIn("Big workshop project") 86 | ) 87 | ), 88 | 89 | slide( 90 | "How it works", 91 | Enumeration( 92 | Item.stable("lectures are split into smaller chapters"), 93 | Item.fadeIn("exercises are mixed in"), 94 | Item.fadeIn("question and code exercises") 95 | ) 96 | ), 97 | 98 | slide( 99 | "How it works", 100 | Enumeration( 101 | Item.stable("we will do a lunch break at 13:00"), 102 | Item.fadeIn("5 to 10 minute break every hour"), 103 | Item.fadeIn("or just ask :)") 104 | ) 105 | ), 106 | 107 | slide( 108 | "How it works", 109 | <.h3("If you have a question ask it directly!") 110 | ), 111 | 112 | noHeaderSlide( 113 | <.h3("Feedback") 114 | ), 115 | 116 | slide( 117 | "Feedback", 118 | <.h4("Are the lectures helpful?"), 119 | <.br, 120 | <.br, 121 | <.h4("What would you change?") 122 | ), 123 | 124 | slide( 125 | "Feedback", 126 | <.p("Give direct feedback, or send us a mail.") 127 | ) 128 | ) 129 | 130 | val exercises = chapter( 131 | chapterSlide( 132 | <.h2("Coding Exercises") 133 | ), 134 | 135 | slide( 136 | "Coding Exercises", 137 | <.p("All exercise related code can be found in the exercise-directories.") 138 | ), 139 | 140 | slide( 141 | "Coding Exercises", 142 | <.p("We use SBT to build our code and execute tests."), 143 | <.br, 144 | <.img( 145 | ^.alt := "SBT logo", 146 | ^.src := "./img/sbt_logo.svg" 147 | ) 148 | ), 149 | 150 | noHeaderSlide( 151 | <.h3("The Interactive Build Tool"), 152 | <.br, 153 | <.p( 154 | ^.cls := "fragment fade-in", 155 | "Formerly: The Simple Build Tool" 156 | ) 157 | ), 158 | 159 | slide( 160 | "SBT: simple project", 161 | bash(""" 162 | my-project/ 163 | # project information (sbt version, plugins, ...) 164 | project/ 165 | 166 | # source code directory 167 | src/ 168 | # compiled results 169 | target/ 170 | 171 | # build file defining the whole project 172 | build.sbt 173 | """) 174 | ), 175 | 176 | slide( 177 | "SBT: build file", 178 | scalaC(""" 179 | // build.sbt 180 | scalaVersion := "2.12.6" 181 | name := "my-project" 182 | 183 | libraryDependencies ++= Seq(...) 184 | """) 185 | ), 186 | 187 | slide( 188 | "SBT: sub-projects", 189 | bash(""" 190 | my-project/ 191 | project/ 192 | 193 | # sub projects defined in the build file 194 | sub-project-0/ 195 | src/ 196 | target/ 197 | sub-project-1/ 198 | ... 199 | 200 | build.sbt 201 | """) 202 | ), 203 | 204 | slide( 205 | "SBT: build file", 206 | scalaC(""" 207 | // build.sbt 208 | val common = Seq( 209 | scalaVersion := "2.12.6" 210 | ) 211 | 212 | lazy val root = project 213 | .in(file(".")) 214 | .aggregate(subProject0, ...) 215 | 216 | lazy val subProject0 = project 217 | .in(file("sub-project-0")) 218 | .settings( 219 | common, 220 | libraryDependencies ++= Seq(...) 221 | ) 222 | ... 223 | """) 224 | ), 225 | 226 | slide( 227 | "SBT: keep it running", 228 | <.p("SBT needs a lot of time to start ... so keep it running."), 229 | <.br, 230 | bash(""" 231 | cd 232 | 233 | # opens a cli 234 | sbt 235 | """) 236 | ), 237 | 238 | slide( 239 | "SBT: commands", 240 | bash(""" 241 | # show all sub-projects 242 | sbt> projects 243 | 244 | # change to sub-project 245 | sbt> project 246 | 247 | # compile source code 248 | sbt> compile 249 | 250 | # compile on file change 251 | sbt> ~compile 252 | """) 253 | ), 254 | 255 | slide( 256 | "SBT: commands", 257 | bash(""" 258 | # execute all tests 259 | sbt> test 260 | 261 | # execute a single test 262 | sbt> test:testOnly 263 | """) 264 | ), 265 | 266 | slide( 267 | "SBT: exercise", 268 | bash(""" 269 | sbt> project 270 | sbt> compile 271 | sbt> test:testOnly 272 | """) 273 | ), 274 | 275 | slide( 276 | "Exercise Structure", 277 | Enumeration( 278 | Item.stable(<.p("exercise code in main dir, e.g. `exercise1_scala_101/src/", font(^.color := "red", "main"), "/scala/exercises1/`")), 279 | Item.fadeIn(<.p("solutions are available in the test dir, e.g. `exercise1_scala_101/src/", font(^.color := "red", "test"), "/scala/exercises1/`")) 280 | ) 281 | ), 282 | 283 | noHeaderSlide( 284 | <.h3("That is all about SBT for now") 285 | ) 286 | ) 287 | 288 | val repl = chapter( 289 | chapterSlide( 290 | <.h2("REPL") 291 | ), 292 | 293 | slide( 294 | "Repl", 295 | <.p("You can use a Scala REPL (Read-Evaluate-Print-Loop) to try code you see during the lectures.") 296 | ), 297 | 298 | noHeaderSlide( 299 | <.h3("Buil-In Scala REPL") 300 | ), 301 | 302 | slide( 303 | "Scala REPL", 304 | bash(""" 305 | # just execute Scala 306 | $>scala 307 | """), 308 | scalaC(""" 309 | scala> 1 + 1 310 | res0: Int = 2 311 | 312 | // exit with 313 | scala> :q 314 | """) 315 | ), 316 | 317 | noHeaderSlide( 318 | <.h3("Ammonite") 319 | ), 320 | 321 | slide( 322 | "Ammonite", 323 | bash(""" 324 | $> amm 325 | """), 326 | scalaC(""" 327 | @ 1 + 1 328 | res0: Int = 2 329 | 330 | @ exit 331 | """) 332 | ), 333 | 334 | noHeaderSlide( 335 | <.h2("Any questions so far?") 336 | ) 337 | ) 338 | 339 | val Show = ScalaComponent 340 | .builder[Unit]("Slideshow") 341 | .renderStatic( 342 | <.div( 343 | ^.cls := "reveal", 344 | <.div( 345 | ^.cls := "slides", 346 | introduction, 347 | exercises, 348 | repl 349 | ) 350 | ) 351 | ) 352 | .build 353 | 354 | @JSExport 355 | override def main(): Unit = { 356 | Show().renderIntoDOM(dom.document.body) 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /lecture1_scala_101/img/java-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 56 | 59 | 61 | 62 | 64 | 68 | 69 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /lecture1_scala_101/img/learning_curve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/lecture1_scala_101/img/learning_curve.jpg -------------------------------------------------------------------------------- /lecture1_scala_101/img/principles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 35 | 36 | 44 | 50 | 51 | 52 | 74 | 76 | 77 | 79 | image/svg+xml 80 | 82 | 83 | 84 | 85 | 86 | 91 | 107 | Object OrientedProgramming 123 | Functional Programming 139 | Types 150 | 156 | 162 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /lecture1_scala_101/img/scala-paper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/lecture1_scala_101/img/scala-paper.png -------------------------------------------------------------------------------- /lecture1_scala_101/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scala 101 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lecture2_fp_101/img/indirect_rec.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 29 | 35 | 36 | 44 | 50 | 51 | 59 | 65 | 66 | 74 | 80 | 81 | 89 | 95 | 96 | 104 | 110 | 111 | 120 | 126 | 127 | 136 | 142 | 143 | 151 | 157 | 158 | 166 | 172 | 173 | 187 | 188 | 210 | 212 | 213 | 215 | image/svg+xml 216 | 218 | 219 | 220 | 221 | 222 | 227 | 231 | 237 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /lecture2_fp_101/img/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 67 | 0 78 | 85 | 92 | 99 | 106 | 113 | Nil 124 | 1 135 | 2 146 | 147 | 148 | -------------------------------------------------------------------------------- /lecture2_fp_101/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FP 101 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lecture3_std_lib/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Standard Library 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lecture4_typeclasses_101/img/yeah.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/lecture4_typeclasses_101/img/yeah.gif -------------------------------------------------------------------------------- /lecture4_typeclasses_101/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Typeclass 101 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lecture5_typeclasses_incarnations/img/cats-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/lecture5_typeclasses_incarnations/img/cats-logo.png -------------------------------------------------------------------------------- /lecture5_typeclasses_incarnations/img/monoids-monoids-everywhere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/lecture5_typeclasses_incarnations/img/monoids-monoids-everywhere.jpg -------------------------------------------------------------------------------- /lecture5_typeclasses_incarnations/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Typeclass Incarnations 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lecture6_side_effects/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Side Effects 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lecture6_side_effects/src/main/scala/SideEffectsLecture.scala: -------------------------------------------------------------------------------- 1 | 2 | import PresentationUtil._ 3 | import japgolly.scalajs.react.ScalaComponent 4 | import japgolly.scalajs.react.vdom.html_<^._ 5 | import org.scalajs.dom 6 | 7 | import scala.scalajs.js.JSApp 8 | import scala.scalajs.js.annotation.JSExport 9 | 10 | object SideEffectsLecture extends JSApp { 11 | 12 | import Enumeration._ 13 | 14 | val overview = chapter( 15 | chapterSlide( 16 | <.h1("Side Effects") 17 | ), 18 | 19 | slide( 20 | "What we will learn in this lecture", 21 | Enumeration( 22 | Item.stable("Side Effects"), 23 | Item.fadeIn("Pros/Cons"), 24 | Item.fadeIn("Functional Side Effects"), 25 | Item.fadeIn("Side Effects in Code"), 26 | ) 27 | ) 28 | ) 29 | 30 | val introduction = chapter( 31 | chapterSlide( 32 | <.h2("Side Effects"), 33 | <.h3("What Now?") 34 | ), 35 | 36 | slide( 37 | "What are side effects", 38 | <.h3("effects outside of the local scope of a function") 39 | ), 40 | 41 | slide( 42 | "Side effects: setting a var", 43 | scalaC(""" 44 | var x = 0 45 | 46 | def inc(): Int = { 47 | x = x + 1 48 | x 49 | } 50 | """) 51 | ), 52 | 53 | slide( 54 | "Side effects: interacting with the OS", 55 | scalaC(""" 56 | println("Hello") 57 | readLine() 58 | """) 59 | ) 60 | ) 61 | 62 | val pros = chapter( 63 | chapterSlide( 64 | <.h2("Side Effects: Pros"), 65 | <.h3("But Why?") 66 | ), 67 | 68 | slide( 69 | "Side effects: pros", 70 | Enumeration( 71 | Item.stable("no need - theoretically speaking"), 72 | Item.stable("not practical to work without"), 73 | Enumeration( 74 | Item.stable("interact with \"world\" (read files, send network request)"), 75 | Item.stable("performance of update vs copy") 76 | ) 77 | ) 78 | ), 79 | 80 | noHeaderSlide( 81 | <.h3("Ah, Ok.") 82 | ) 83 | ) 84 | 85 | val cons = chapter( 86 | chapterSlide( 87 | <.h2("Side Effects: Cons"), 88 | <.h3("So Why Not?") 89 | ), 90 | 91 | slide( 92 | "Side effects: cons", 93 | <.h3("loss of referential transparency"), 94 | scalaC(""" 95 | f(g(), g()) != { val a = g(); f(a, a) } 96 | """) 97 | ), 98 | 99 | slide( 100 | "Side effects: cons", 101 | <.h3("common source of misunderstandings/bugs"), 102 | scalaC(""" 103 | seq.grouped(3) 104 | != 105 | { 106 | val g = seq.grouped(3) 107 | val s = seq.size 108 | g 109 | } 110 | """) 111 | ), 112 | 113 | slide( 114 | "Side effects: cons", 115 | <.h3("loss of optimization opportunities"), 116 | scalaC(""" 117 | var cnt = 0 118 | for (el <- seq) { 119 | cnt = cnt + 1 120 | } 121 | """), 122 | <.h4("vs"), 123 | scalaC(""" 124 | seq.reduce((l, r) => l + r) 125 | """) 126 | ) 127 | ) 128 | 129 | val functional = chapter( 130 | chapterSlide( 131 | <.h2("Functional Side Effects"), 132 | <.h3("Interesting! So What Now?") 133 | ), 134 | 135 | slide( 136 | "Functional side effects", 137 | <.h3("no side effects in our functional programs") 138 | ), 139 | 140 | slide( 141 | "Functional side effects", 142 | <.h3("instead: store intended side effect for execution \"some time later\"") 143 | ) 144 | ) 145 | 146 | val code = chapter( 147 | chapterSlide( 148 | <.h2("Side Effects in Code"), 149 | <.h3("Ok. One Last Question: What?") 150 | ), 151 | 152 | slide( 153 | "Side effects in code", 154 | <.h2("Introduce IO[A]"), 155 | <.h3("side effect(s) resulting in a value of type A") 156 | ), 157 | 158 | slide( 159 | "Side effects in code", 160 | scalaC(""" 161 | println("Hello World"): IO[Unit] 162 | 163 | readLine(): IO[String] 164 | """) 165 | ), 166 | 167 | slide( 168 | "Consequences of IO[A]", 169 | <.h3("restoration of referential transparency"), 170 | scalaC(""" 171 | f(println(), println()) == { val pl = println(); f(pl, pl) } 172 | """) 173 | ), 174 | 175 | slide( 176 | "Consequences of IO[A]", 177 | <.h3("forcing marked code/handling of effects"), 178 | <.h4("compare to null vs None") 179 | ), 180 | 181 | noHeaderSlide( 182 | <.h2("Example"), 183 | <.h3("Neat! Let's Use It.") 184 | ), 185 | 186 | slide( 187 | "Example", 188 | scalaC(""" 189 | val name = readLine() 190 | 191 | println("Hello" + name) 192 | """) 193 | ), 194 | 195 | slide( 196 | "Example", 197 | scalaC(""" 198 | val name = readLine() //: IO[String] 199 | 200 | println("Hello" + name) //: IO[Unit] 201 | """) 202 | ), 203 | 204 | slide( 205 | "Example", 206 | scalaC(""" 207 | for { 208 | name <- readLine() 209 | 210 | _ <- println("Hello" + name) 211 | } yield () //: IO[Unit] 212 | """) 213 | ) 214 | ) 215 | 216 | val Show = ScalaComponent 217 | .builder[Unit]("Slideshow") 218 | .renderStatic( 219 | <.div( 220 | ^.cls := "reveal", 221 | <.div( 222 | ^.cls := "slides", 223 | overview, 224 | introduction, 225 | pros, 226 | cons, 227 | functional, 228 | code 229 | ) 230 | ) 231 | ) 232 | .build 233 | 234 | @JSExport 235 | override def main(): Unit = { 236 | Show().renderIntoDOM(dom.document.body) 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /lecture7_io/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IO 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lectures-shared/src/main/scala/PresentationUtil.scala: -------------------------------------------------------------------------------- 1 | 2 | import japgolly.scalajs.react._ 3 | import japgolly.scalajs.react.vdom.html_<^._ 4 | import japgolly.scalajs.react.vdom.TagOf 5 | import org.scalajs.dom 6 | import dom.raw.HTMLElement 7 | 8 | object PresentationUtil { 9 | 10 | val font = HtmlTag("font") 11 | 12 | val dataBackground = VdomAttr("data-background") 13 | val dataBackgroundColor = VdomAttr("data-background-color") 14 | val dataBackgroundSize = VdomAttr("data-background-size") 15 | val dataTrim = VdomAttr("data-trim") := "" 16 | val dataNoEscape = VdomAttr("data-noescape") := "" 17 | 18 | def chapter(slides: TagOf[HTMLElement]*): TagOf[HTMLElement] = <.section(slides: _*) 19 | 20 | def header(text: String, cls: String): TagOf[HTMLElement] = 21 | <.div( 22 | ^.cls := cls, 23 | <.img( 24 | ^.src := "./../reveal/img/logo.svg", 25 | ^.alt := "Scala Summer School logo" 26 | ), 27 | <.p(text) 28 | ) 29 | 30 | // 100% side-effect full 31 | private def removeHeader(): Unit = { 32 | val headerElements = dom.document.getElementsByClassName("slide-header") 33 | 34 | (0 until headerElements.length).foreach { id => 35 | val element = headerElements(id) 36 | 37 | element.parentNode.removeChild(element) 38 | } 39 | } 40 | 41 | private def cleanSlide(content: TagOf[HTMLElement]): TagOf[HTMLElement] = { 42 | removeHeader() 43 | 44 | content 45 | } 46 | 47 | private val ChapterSlideProps = Seq( 48 | (dataBackground := "./../reveal/img/dark-logo.svg"), 49 | (dataBackgroundColor := "#363633"), 50 | (dataBackgroundSize := "30%") 51 | ) 52 | 53 | def chapterSlide(content: TagOf[HTMLElement]*): TagOf[HTMLElement] = cleanSlide( 54 | <.section( 55 | (ChapterSlideProps ++: content): _* 56 | ) 57 | ) 58 | 59 | def noHeaderSlide(content: TagOf[HTMLElement]*): TagOf[HTMLElement] = cleanSlide( 60 | <.section( 61 | content: _* 62 | ) 63 | ) 64 | 65 | def exerciseSlide(headerStr: String, content: TagOf[HTMLElement]*): TagOf[HTMLElement] = cleanSlide( 66 | <.section( 67 | (header(headerStr, "exercise-header") +: content): _* 68 | ) 69 | ) 70 | 71 | def slide(headerStr: String, content: TagOf[HTMLElement]*): TagOf[HTMLElement] = cleanSlide( 72 | <.section( 73 | (header(headerStr, "slide-header") +: content): _* 74 | ) 75 | ) 76 | 77 | private def rawCode(language: String, codeStr: String): TagOf[HTMLElement] = 78 | <.code( 79 | ^.cls := language, 80 | dataTrim, 81 | dataNoEscape, 82 | codeStr 83 | ) 84 | 85 | def scalaC(codeStr: String): TagOf[HTMLElement] = <.pre(rawCode("Scala", codeStr)) 86 | 87 | def scalaCFragment(codeStr: String): TagOf[HTMLElement] = 88 | <.pre( 89 | ^.cls := "fragment fade-in", 90 | rawCode("Scala", codeStr) 91 | ) 92 | 93 | def bash(codeStr: String): TagOf[HTMLElement] = <.pre(rawCode("Bash", codeStr)) 94 | 95 | object Enumeration { 96 | 97 | object Item { 98 | 99 | def stable(content: TagOf[HTMLElement]): TagOf[HTMLElement] = <.li(content) 100 | def fadeIn(content: TagOf[HTMLElement]): TagOf[HTMLElement] = <.li(^.cls := "fragment fade-in", content) 101 | def stable(content: String): TagOf[HTMLElement] = <.li(<.p(content)) 102 | def fadeIn(content: String): TagOf[HTMLElement] = <.li(^.cls := "fragment fade-in", <.p(content)) 103 | } 104 | 105 | def apply(head: TagOf[HTMLElement], tail: TagOf[HTMLElement]*): TagOf[HTMLElement] = { 106 | <.ul( 107 | (head +: tail): _* 108 | ) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.1.5 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.24") 2 | -------------------------------------------------------------------------------- /reveal/css/presentation.css: -------------------------------------------------------------------------------- 1 | .reveal section pre, 2 | .reveal section img, 3 | .slide-header img { 4 | background: none; 5 | border: none; 6 | box-shadow: none; 7 | } 8 | 9 | .slide-header, 10 | .exercise-header { 11 | position: fixed; 12 | top: -3%; 13 | left: -16%; 14 | width: 140%; 15 | } 16 | 17 | .slide-header { 18 | background-color: #3e3e3b; 19 | } 20 | 21 | .exercise-header { 22 | background-color: #e67e22; 23 | } 24 | 25 | .slide-header > p, 26 | .exercise-header > p { 27 | position: relative; 28 | float: left; 29 | margin: 0; 30 | padding-top: 15px; 31 | padding-bottom: 11px; 32 | padding-left: 20px; 33 | font-size: 70%; 34 | } 35 | 36 | .slide-header > p { 37 | color: #ffffff; 38 | } 39 | 40 | .exercise-header > p { 41 | color: #ffffff; 42 | } 43 | 44 | .slide-header > img, 45 | .exercise-header > img { 46 | position: relative; 47 | float: left; 48 | width: 33px; 49 | padding-left: 55px; 50 | } 51 | 52 | .reveal section ul li > p { 53 | margin-left: 0; 54 | margin-right: 0; 55 | } 56 | 57 | .team-img { 58 | height: 20%; 59 | border-radius: 50%; 60 | vertical-align: middle; 61 | margin-right: 10px; 62 | margin-bottom: 10px; 63 | } 64 | 65 | .florian-img { 66 | height: 300px; 67 | } 68 | 69 | /* ########## Queries */ 70 | @media only screen and (min-width: 600px) { 71 | .slide-header img, 72 | .exercise-header img { 73 | padding-left: 35px; 74 | } 75 | } 76 | 77 | @media only screen and (min-width: 1500px) { 78 | .slide-header img, 79 | .exercise-header img { 80 | padding-left: 40px; 81 | } 82 | } 83 | 84 | -------------------------------------------------------------------------------- /reveal/css/theme/source/white.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * White theme for reveal.js. This is the opposite of the 'black' theme. 3 | * 4 | * By Hakim El Hattab, http://hakim.se 5 | */ 6 | 7 | 8 | // Default mixins and settings ----------------- 9 | @import "../template/mixins"; 10 | @import "../template/settings"; 11 | // --------------------------------------------- 12 | 13 | 14 | // Include theme-specific fonts 15 | @import url(../../lib/font/source-sans-pro/source-sans-pro.css); 16 | 17 | 18 | // Override theme settings (see ../template/settings.scss) 19 | $backgroundColor: #fff; 20 | 21 | $mainColor: #222; 22 | $headingColor: #222; 23 | 24 | $mainFontSize: 42px; 25 | $mainFont: 'Source Sans Pro', Helvetica, sans-serif; 26 | $headingFont: 'Source Sans Pro', Helvetica, sans-serif; 27 | $headingTextShadow: none; 28 | $headingLetterSpacing: normal; 29 | $headingTextTransform: uppercase; 30 | $headingFontWeight: 600; 31 | $linkColor: #2a76dd; 32 | $linkColorHover: lighten( $linkColor, 15% ); 33 | $selectionBackgroundColor: lighten( $linkColor, 25% ); 34 | 35 | $heading1Size: 2.5em; 36 | $heading2Size: 1.6em; 37 | $heading3Size: 1.3em; 38 | $heading4Size: 1.0em; 39 | 40 | section.has-dark-background { 41 | &, h1, h2, h3, h4, h5, h6 { 42 | color: #fff; 43 | } 44 | } 45 | 46 | 47 | // Theme template ------------------------------ 48 | @import "../template/theme"; 49 | // --------------------------------------------- -------------------------------------------------------------------------------- /reveal/css/theme/template/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin vertical-gradient( $top, $bottom ) { 2 | background: $top; 3 | background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); 4 | background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); 5 | background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); 6 | background: -o-linear-gradient( top, $top 0%, $bottom 100% ); 7 | background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); 8 | background: linear-gradient( top, $top 0%, $bottom 100% ); 9 | } 10 | 11 | @mixin horizontal-gradient( $top, $bottom ) { 12 | background: $top; 13 | background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); 14 | background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); 15 | background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); 16 | background: -o-linear-gradient( left, $top 0%, $bottom 100% ); 17 | background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); 18 | background: linear-gradient( left, $top 0%, $bottom 100% ); 19 | } 20 | 21 | @mixin radial-gradient( $outer, $inner, $type: circle ) { 22 | background: $outer; 23 | background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 24 | background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); 25 | background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 26 | background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 27 | background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 28 | background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); 29 | } -------------------------------------------------------------------------------- /reveal/css/theme/template/settings.scss: -------------------------------------------------------------------------------- 1 | // Base settings for all themes that can optionally be 2 | // overridden by the super-theme 3 | 4 | // Background of the presentation 5 | $backgroundColor: #2b2b2b; 6 | 7 | // Primary/body text 8 | $mainFont: 'Lato', sans-serif; 9 | $mainFontSize: 40px; 10 | $mainColor: #eee; 11 | 12 | // Vertical spacing between blocks of text 13 | $blockMargin: 20px; 14 | 15 | // Headings 16 | $headingMargin: 0 0 $blockMargin 0; 17 | $headingFont: 'League Gothic', Impact, sans-serif; 18 | $headingColor: #eee; 19 | $headingLineHeight: 1.2; 20 | $headingLetterSpacing: normal; 21 | $headingTextTransform: uppercase; 22 | $headingTextShadow: none; 23 | $headingFontWeight: normal; 24 | $heading1TextShadow: $headingTextShadow; 25 | 26 | $heading1Size: 3.77em; 27 | $heading2Size: 2.11em; 28 | $heading3Size: 1.55em; 29 | $heading4Size: 1.00em; 30 | 31 | // Links and actions 32 | $linkColor: #13DAEC; 33 | $linkColorHover: lighten( $linkColor, 20% ); 34 | 35 | // Text selection 36 | $selectionBackgroundColor: #FF5E99; 37 | $selectionColor: #fff; 38 | 39 | // Generates the presentation background, can be overridden 40 | // to return a background image or gradient 41 | @mixin bodyBackground() { 42 | background: $backgroundColor; 43 | } -------------------------------------------------------------------------------- /reveal/css/theme/template/theme.scss: -------------------------------------------------------------------------------- 1 | // Base theme template for reveal.js 2 | 3 | /********************************************* 4 | * GLOBAL STYLES 5 | *********************************************/ 6 | 7 | body { 8 | @include bodyBackground(); 9 | background-color: $backgroundColor; 10 | } 11 | 12 | .reveal { 13 | font-family: $mainFont; 14 | font-size: $mainFontSize; 15 | font-weight: normal; 16 | color: $mainColor; 17 | } 18 | 19 | ::selection { 20 | color: $selectionColor; 21 | background: $selectionBackgroundColor; 22 | text-shadow: none; 23 | } 24 | 25 | ::-moz-selection { 26 | color: $selectionColor; 27 | background: $selectionBackgroundColor; 28 | text-shadow: none; 29 | } 30 | 31 | .reveal .slides>section, 32 | .reveal .slides>section>section { 33 | line-height: 1.3; 34 | font-weight: inherit; 35 | } 36 | 37 | /********************************************* 38 | * HEADERS 39 | *********************************************/ 40 | 41 | .reveal h1, 42 | .reveal h2, 43 | .reveal h3, 44 | .reveal h4, 45 | .reveal h5, 46 | .reveal h6 { 47 | margin: $headingMargin; 48 | color: $headingColor; 49 | 50 | font-family: $headingFont; 51 | font-weight: $headingFontWeight; 52 | line-height: $headingLineHeight; 53 | letter-spacing: $headingLetterSpacing; 54 | 55 | text-transform: $headingTextTransform; 56 | text-shadow: $headingTextShadow; 57 | 58 | word-wrap: break-word; 59 | } 60 | 61 | .reveal h1 {font-size: $heading1Size; } 62 | .reveal h2 {font-size: $heading2Size; } 63 | .reveal h3 {font-size: $heading3Size; } 64 | .reveal h4 {font-size: $heading4Size; } 65 | 66 | .reveal h1 { 67 | text-shadow: $heading1TextShadow; 68 | } 69 | 70 | 71 | /********************************************* 72 | * OTHER 73 | *********************************************/ 74 | 75 | .reveal p { 76 | margin: $blockMargin 0; 77 | line-height: 1.3; 78 | } 79 | 80 | /* Ensure certain elements are never larger than the slide itself */ 81 | .reveal img, 82 | .reveal video, 83 | .reveal iframe { 84 | max-width: 95%; 85 | max-height: 95%; 86 | } 87 | .reveal strong, 88 | .reveal b { 89 | font-weight: bold; 90 | } 91 | 92 | .reveal em { 93 | font-style: italic; 94 | } 95 | 96 | .reveal ol, 97 | .reveal dl, 98 | .reveal ul { 99 | display: inline-block; 100 | 101 | text-align: left; 102 | margin: 0 0 0 1em; 103 | } 104 | 105 | .reveal ol { 106 | list-style-type: decimal; 107 | } 108 | 109 | .reveal ul { 110 | list-style-type: disc; 111 | } 112 | 113 | .reveal ul ul { 114 | list-style-type: square; 115 | } 116 | 117 | .reveal ul ul ul { 118 | list-style-type: circle; 119 | } 120 | 121 | .reveal ul ul, 122 | .reveal ul ol, 123 | .reveal ol ol, 124 | .reveal ol ul { 125 | display: block; 126 | margin-left: 40px; 127 | } 128 | 129 | .reveal dt { 130 | font-weight: bold; 131 | } 132 | 133 | .reveal dd { 134 | margin-left: 40px; 135 | } 136 | 137 | .reveal q, 138 | .reveal blockquote { 139 | quotes: none; 140 | } 141 | 142 | .reveal blockquote { 143 | display: block; 144 | position: relative; 145 | width: 70%; 146 | margin: $blockMargin auto; 147 | padding: 5px; 148 | 149 | font-style: italic; 150 | background: rgba(255, 255, 255, 0.05); 151 | box-shadow: 0px 0px 2px rgba(0,0,0,0.2); 152 | } 153 | .reveal blockquote p:first-child, 154 | .reveal blockquote p:last-child { 155 | display: inline-block; 156 | } 157 | 158 | .reveal q { 159 | font-style: italic; 160 | } 161 | 162 | .reveal pre { 163 | display: block; 164 | position: relative; 165 | width: 90%; 166 | margin: $blockMargin auto; 167 | 168 | text-align: left; 169 | font-size: 0.55em; 170 | font-family: monospace; 171 | line-height: 1.2em; 172 | 173 | word-wrap: break-word; 174 | 175 | box-shadow: 0px 0px 6px rgba(0,0,0,0.3); 176 | } 177 | .reveal code { 178 | font-family: monospace; 179 | } 180 | 181 | .reveal pre code { 182 | display: block; 183 | padding: 5px; 184 | overflow: auto; 185 | max-height: 400px; 186 | word-wrap: normal; 187 | } 188 | 189 | .reveal table { 190 | margin: auto; 191 | border-collapse: collapse; 192 | border-spacing: 0; 193 | } 194 | 195 | .reveal table th { 196 | font-weight: bold; 197 | } 198 | 199 | .reveal table th, 200 | .reveal table td { 201 | text-align: left; 202 | padding: 0.2em 0.5em 0.2em 0.5em; 203 | border-bottom: 1px solid; 204 | } 205 | 206 | .reveal table th[align="center"], 207 | .reveal table td[align="center"] { 208 | text-align: center; 209 | } 210 | 211 | .reveal table th[align="right"], 212 | .reveal table td[align="right"] { 213 | text-align: right; 214 | } 215 | 216 | .reveal table tbody tr:last-child th, 217 | .reveal table tbody tr:last-child td { 218 | border-bottom: none; 219 | } 220 | 221 | .reveal sup { 222 | vertical-align: super; 223 | } 224 | .reveal sub { 225 | vertical-align: sub; 226 | } 227 | 228 | .reveal small { 229 | display: inline-block; 230 | font-size: 0.6em; 231 | line-height: 1.2em; 232 | vertical-align: top; 233 | } 234 | 235 | .reveal small * { 236 | vertical-align: top; 237 | } 238 | 239 | 240 | /********************************************* 241 | * LINKS 242 | *********************************************/ 243 | 244 | .reveal a { 245 | color: $linkColor; 246 | text-decoration: none; 247 | 248 | -webkit-transition: color .15s ease; 249 | -moz-transition: color .15s ease; 250 | transition: color .15s ease; 251 | } 252 | .reveal a:hover { 253 | color: $linkColorHover; 254 | 255 | text-shadow: none; 256 | border: none; 257 | } 258 | 259 | .reveal .roll span:after { 260 | color: #fff; 261 | background: darken( $linkColor, 15% ); 262 | } 263 | 264 | 265 | /********************************************* 266 | * IMAGES 267 | *********************************************/ 268 | 269 | .reveal section img { 270 | margin: 15px 0px; 271 | background: rgba(255,255,255,0.12); 272 | border: 4px solid $mainColor; 273 | 274 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); 275 | } 276 | 277 | .reveal section img.plain { 278 | border: 0; 279 | box-shadow: none; 280 | } 281 | 282 | .reveal a img { 283 | -webkit-transition: all .15s linear; 284 | -moz-transition: all .15s linear; 285 | transition: all .15s linear; 286 | } 287 | 288 | .reveal a:hover img { 289 | background: rgba(255,255,255,0.2); 290 | border-color: $linkColor; 291 | 292 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); 293 | } 294 | 295 | 296 | /********************************************* 297 | * NAVIGATION CONTROLS 298 | *********************************************/ 299 | 300 | .reveal .controls .navigate-left, 301 | .reveal .controls .navigate-left.enabled { 302 | border-right-color: $linkColor; 303 | } 304 | 305 | .reveal .controls .navigate-right, 306 | .reveal .controls .navigate-right.enabled { 307 | border-left-color: $linkColor; 308 | } 309 | 310 | .reveal .controls .navigate-up, 311 | .reveal .controls .navigate-up.enabled { 312 | border-bottom-color: $linkColor; 313 | } 314 | 315 | .reveal .controls .navigate-down, 316 | .reveal .controls .navigate-down.enabled { 317 | border-top-color: $linkColor; 318 | } 319 | 320 | .reveal .controls .navigate-left.enabled:hover { 321 | border-right-color: $linkColorHover; 322 | } 323 | 324 | .reveal .controls .navigate-right.enabled:hover { 325 | border-left-color: $linkColorHover; 326 | } 327 | 328 | .reveal .controls .navigate-up.enabled:hover { 329 | border-bottom-color: $linkColorHover; 330 | } 331 | 332 | .reveal .controls .navigate-down.enabled:hover { 333 | border-top-color: $linkColorHover; 334 | } 335 | 336 | 337 | /********************************************* 338 | * PROGRESS BAR 339 | *********************************************/ 340 | 341 | .reveal .progress { 342 | background: rgba(0,0,0,0.2); 343 | } 344 | .reveal .progress span { 345 | background: $linkColor; 346 | 347 | -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 348 | -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 349 | transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); 350 | } 351 | 352 | 353 | -------------------------------------------------------------------------------- /reveal/css/theme/white.css: -------------------------------------------------------------------------------- 1 | /** 2 | * White theme for reveal.js. This is the opposite of the 'black' theme. 3 | * 4 | * By Hakim El Hattab, http://hakim.se 5 | */ 6 | @import url(../../lib/font/source-sans-pro/source-sans-pro.css); 7 | section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { 8 | color: #fff; } 9 | 10 | /********************************************* 11 | * GLOBAL STYLES 12 | *********************************************/ 13 | body { 14 | background: #fff; 15 | background-color: #fff; } 16 | 17 | .reveal { 18 | font-family: "Source Sans Pro", Helvetica, sans-serif; 19 | font-size: 42px; 20 | font-weight: normal; 21 | color: #222; } 22 | 23 | ::selection { 24 | color: #fff; 25 | background: #98bdef; 26 | text-shadow: none; } 27 | 28 | ::-moz-selection { 29 | color: #fff; 30 | background: #98bdef; 31 | text-shadow: none; } 32 | 33 | .reveal .slides > section, 34 | .reveal .slides > section > section { 35 | line-height: 1.3; 36 | font-weight: inherit; } 37 | 38 | /********************************************* 39 | * HEADERS 40 | *********************************************/ 41 | .reveal h1, 42 | .reveal h2, 43 | .reveal h3, 44 | .reveal h4, 45 | .reveal h5, 46 | .reveal h6 { 47 | margin: 0 0 20px 0; 48 | color: #222; 49 | font-family: "Source Sans Pro", Helvetica, sans-serif; 50 | font-weight: 600; 51 | line-height: 1.2; 52 | letter-spacing: normal; 53 | text-transform: uppercase; 54 | text-shadow: none; 55 | word-wrap: break-word; } 56 | 57 | .reveal h1 { 58 | font-size: 2.5em; } 59 | 60 | .reveal h2 { 61 | font-size: 1.6em; } 62 | 63 | .reveal h3 { 64 | font-size: 1.3em; } 65 | 66 | .reveal h4 { 67 | font-size: 1em; } 68 | 69 | .reveal h1 { 70 | text-shadow: none; } 71 | 72 | /********************************************* 73 | * OTHER 74 | *********************************************/ 75 | .reveal p { 76 | margin: 20px 0; 77 | line-height: 1.3; } 78 | 79 | /* Ensure certain elements are never larger than the slide itself */ 80 | .reveal img, 81 | .reveal video, 82 | .reveal iframe { 83 | max-width: 95%; 84 | max-height: 95%; } 85 | 86 | .reveal strong, 87 | .reveal b { 88 | font-weight: bold; } 89 | 90 | .reveal em { 91 | font-style: italic; } 92 | 93 | .reveal ol, 94 | .reveal dl, 95 | .reveal ul { 96 | display: inline-block; 97 | text-align: left; 98 | margin: 0 0 0 1em; } 99 | 100 | .reveal ol { 101 | list-style-type: decimal; } 102 | 103 | .reveal ul { 104 | list-style-type: disc; } 105 | 106 | .reveal ul ul { 107 | list-style-type: square; } 108 | 109 | .reveal ul ul ul { 110 | list-style-type: circle; } 111 | 112 | .reveal ul ul, 113 | .reveal ul ol, 114 | .reveal ol ol, 115 | .reveal ol ul { 116 | display: block; 117 | margin-left: 40px; } 118 | 119 | .reveal dt { 120 | font-weight: bold; } 121 | 122 | .reveal dd { 123 | margin-left: 40px; } 124 | 125 | .reveal q, 126 | .reveal blockquote { 127 | quotes: none; } 128 | 129 | .reveal blockquote { 130 | display: block; 131 | position: relative; 132 | width: 70%; 133 | margin: 20px auto; 134 | padding: 5px; 135 | font-style: italic; 136 | background: rgba(255, 255, 255, 0.05); 137 | box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } 138 | 139 | .reveal blockquote p:first-child, 140 | .reveal blockquote p:last-child { 141 | display: inline-block; } 142 | 143 | .reveal q { 144 | font-style: italic; } 145 | 146 | .reveal pre { 147 | display: block; 148 | position: relative; 149 | width: 90%; 150 | margin: 20px auto; 151 | text-align: left; 152 | font-size: 0.55em; 153 | font-family: monospace; 154 | line-height: 1.2em; 155 | word-wrap: break-word; 156 | box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); } 157 | 158 | .reveal code { 159 | font-family: monospace; } 160 | 161 | .reveal pre code { 162 | display: block; 163 | padding: 5px; 164 | overflow: auto; 165 | max-height: 400px; 166 | word-wrap: normal; } 167 | 168 | .reveal table { 169 | margin: auto; 170 | border-collapse: collapse; 171 | border-spacing: 0; } 172 | 173 | .reveal table th { 174 | font-weight: bold; } 175 | 176 | .reveal table th, 177 | .reveal table td { 178 | text-align: left; 179 | padding: 0.2em 0.5em 0.2em 0.5em; 180 | border-bottom: 1px solid; } 181 | 182 | .reveal table th[align="center"], 183 | .reveal table td[align="center"] { 184 | text-align: center; } 185 | 186 | .reveal table th[align="right"], 187 | .reveal table td[align="right"] { 188 | text-align: right; } 189 | 190 | .reveal table tbody tr:last-child th, 191 | .reveal table tbody tr:last-child td { 192 | border-bottom: none; } 193 | 194 | .reveal sup { 195 | vertical-align: super; } 196 | 197 | .reveal sub { 198 | vertical-align: sub; } 199 | 200 | .reveal small { 201 | display: inline-block; 202 | font-size: 0.6em; 203 | line-height: 1.2em; 204 | vertical-align: top; } 205 | 206 | .reveal small * { 207 | vertical-align: top; } 208 | 209 | /********************************************* 210 | * LINKS 211 | *********************************************/ 212 | .reveal a { 213 | color: #2a76dd; 214 | text-decoration: none; 215 | -webkit-transition: color .15s ease; 216 | -moz-transition: color .15s ease; 217 | transition: color .15s ease; } 218 | 219 | .reveal a:hover { 220 | color: #6ca0e8; 221 | text-shadow: none; 222 | border: none; } 223 | 224 | .reveal .roll span:after { 225 | color: #fff; 226 | background: #1a53a1; } 227 | 228 | /********************************************* 229 | * IMAGES 230 | *********************************************/ 231 | .reveal section img { 232 | margin: 15px 0px; 233 | background: rgba(255, 255, 255, 0.12); 234 | border: 4px solid #222; 235 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } 236 | 237 | .reveal section img.plain { 238 | border: 0; 239 | box-shadow: none; } 240 | 241 | .reveal a img { 242 | -webkit-transition: all .15s linear; 243 | -moz-transition: all .15s linear; 244 | transition: all .15s linear; } 245 | 246 | .reveal a:hover img { 247 | background: rgba(255, 255, 255, 0.2); 248 | border-color: #2a76dd; 249 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } 250 | 251 | /********************************************* 252 | * NAVIGATION CONTROLS 253 | *********************************************/ 254 | .reveal .controls .navigate-left, 255 | .reveal .controls .navigate-left.enabled { 256 | border-right-color: #2a76dd; } 257 | 258 | .reveal .controls .navigate-right, 259 | .reveal .controls .navigate-right.enabled { 260 | border-left-color: #2a76dd; } 261 | 262 | .reveal .controls .navigate-up, 263 | .reveal .controls .navigate-up.enabled { 264 | border-bottom-color: #2a76dd; } 265 | 266 | .reveal .controls .navigate-down, 267 | .reveal .controls .navigate-down.enabled { 268 | border-top-color: #2a76dd; } 269 | 270 | .reveal .controls .navigate-left.enabled:hover { 271 | border-right-color: #6ca0e8; } 272 | 273 | .reveal .controls .navigate-right.enabled:hover { 274 | border-left-color: #6ca0e8; } 275 | 276 | .reveal .controls .navigate-up.enabled:hover { 277 | border-bottom-color: #6ca0e8; } 278 | 279 | .reveal .controls .navigate-down.enabled:hover { 280 | border-top-color: #6ca0e8; } 281 | 282 | /********************************************* 283 | * PROGRESS BAR 284 | *********************************************/ 285 | .reveal .progress { 286 | background: rgba(0, 0, 0, 0.2); } 287 | 288 | .reveal .progress span { 289 | background: #2a76dd; 290 | -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 291 | -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); 292 | transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } 293 | -------------------------------------------------------------------------------- /reveal/img/dark-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 66 | 72 | 78 | 85 | 91 | 97 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /reveal/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 66 | 72 | 78 | 85 | 91 | 97 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /reveal/lib/css/vscode.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:white;color:black}.hljs-comment,.hljs-quote,.hljs-variable{color:#008000}.hljs-keyword,.hljs-selector-tag,.hljs-built_in,.hljs-name,.hljs-tag{color:#00f}.hljs-string,.hljs-title,.hljs-section,.hljs-attribute,.hljs-literal,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-addition{color:#a31515}.hljs-deletion,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-meta{color:#2b91af}.hljs-doctag{color:#808080}.hljs-attr{color:#f00}.hljs-symbol,.hljs-bullet,.hljs-link{color:#00b0e8}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} 2 | -------------------------------------------------------------------------------- /reveal/lib/font/league-gothic/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License (OFL) 2 | http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL 3 | -------------------------------------------------------------------------------- /reveal/lib/font/league-gothic/league-gothic.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'League Gothic'; 3 | src: url('league-gothic.eot'); 4 | src: url('league-gothic.eot?#iefix') format('embedded-opentype'), 5 | url('league-gothic.woff') format('woff'), 6 | url('league-gothic.ttf') format('truetype'); 7 | 8 | font-weight: normal; 9 | font-style: normal; 10 | } -------------------------------------------------------------------------------- /reveal/lib/font/league-gothic/league-gothic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/league-gothic/league-gothic.eot -------------------------------------------------------------------------------- /reveal/lib/font/league-gothic/league-gothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/league-gothic/league-gothic.ttf -------------------------------------------------------------------------------- /reveal/lib/font/league-gothic/league-gothic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/league-gothic/league-gothic.woff -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License 2 | 3 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 7 | 8 | —————————————————————————————- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | —————————————————————————————- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 14 | 15 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 16 | 17 | DEFINITIONS 18 | “Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 19 | 20 | “Reserved Font Name” refers to any names specified as such after the copyright statement(s). 21 | 22 | “Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s). 23 | 24 | “Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 25 | 26 | “Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. 27 | 28 | PERMISSION & CONDITIONS 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 30 | 31 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 32 | 33 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 34 | 35 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 36 | 37 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 38 | 39 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 40 | 41 | TERMINATION 42 | This license becomes null and void if any of the above conditions are not met. 43 | 44 | DISCLAIMER 45 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-italic.eot -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-italic.ttf -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-italic.woff -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-regular.eot -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-regular.ttf -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-regular.woff -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-semibold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-semibold.eot -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-semibold.ttf -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-semibold.woff -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalasummerschool/lectures/e28c8ea20d811a04f93623f29eb949ef941faf16/reveal/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff -------------------------------------------------------------------------------- /reveal/lib/font/source-sans-pro/source-sans-pro.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Source Sans Pro'; 3 | src: url('source-sans-pro-regular.eot'); 4 | src: url('source-sans-pro-regular.eot?#iefix') format('embedded-opentype'), 5 | url('source-sans-pro-regular.woff') format('woff'), 6 | url('source-sans-pro-regular.ttf') format('truetype'); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | @font-face { 12 | font-family: 'Source Sans Pro'; 13 | src: url('source-sans-pro-italic.eot'); 14 | src: url('source-sans-pro-italic.eot?#iefix') format('embedded-opentype'), 15 | url('source-sans-pro-italic.woff') format('woff'), 16 | url('source-sans-pro-italic.ttf') format('truetype'); 17 | font-weight: normal; 18 | font-style: italic; 19 | } 20 | 21 | @font-face { 22 | font-family: 'Source Sans Pro'; 23 | src: url('source-sans-pro-semibold.eot'); 24 | src: url('source-sans-pro-semibold.eot?#iefix') format('embedded-opentype'), 25 | url('source-sans-pro-semibold.woff') format('woff'), 26 | url('source-sans-pro-semibold.ttf') format('truetype'); 27 | font-weight: 600; 28 | font-style: normal; 29 | } 30 | 31 | @font-face { 32 | font-family: 'Source Sans Pro'; 33 | src: url('source-sans-pro-semibolditalic.eot'); 34 | src: url('source-sans-pro-semibolditalic.eot?#iefix') format('embedded-opentype'), 35 | url('source-sans-pro-semibolditalic.woff') format('woff'), 36 | url('source-sans-pro-semibolditalic.ttf') format('truetype'); 37 | font-weight: 600; 38 | font-style: italic; 39 | } -------------------------------------------------------------------------------- /reveal/lib/js/classList.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ 2 | if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(j){var a="classList",f="prototype",m=(j.HTMLElement||j.Element)[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;pn?(i.screensCss.gt&&r("gt-"+n),i.screensCss.gte&&r("gte-"+n)):tt);u.feature("landscape",fe?(i.browserCss.gt&&r("gt-"+f+e),i.browserCss.gte&&r("gte-"+f+e)):h2&&this[u+1]!==t)u&&r(this.slice(u,u+1).join("-").toLowerCase()+i.section);else{var f=n||"index",e=f.indexOf(".");e>0&&(f=f.substring(0,e));c.id=f.toLowerCase()+i.page;u||r("root"+i.section)}});u.screen={height:n.screen.height,width:n.screen.width};tt();b=0;n.addEventListener?n.addEventListener("resize",it,!1):n.attachEvent("onresize",it)})(window); 3 | /*! head.css3 - v1.0.0 */ 4 | (function(n,t){"use strict";function a(n){for(var r in n)if(i[n[r]]!==t)return!0;return!1}function r(n){var t=n.charAt(0).toUpperCase()+n.substr(1),i=(n+" "+c.join(t+" ")+t).split(" ");return!!a(i)}var h=n.document,o=h.createElement("i"),i=o.style,s=" -o- -moz- -ms- -webkit- -khtml- ".split(" "),c="Webkit Moz O ms Khtml".split(" "),l=n.head_conf&&n.head_conf.head||"head",u=n[l],f={gradient:function(){var n="background-image:";return i.cssText=(n+s.join("gradient(linear,left top,right bottom,from(#9f9),to(#fff));"+n)+s.join("linear-gradient(left top,#eee,#fff);"+n)).slice(0,-n.length),!!i.backgroundImage},rgba:function(){return i.cssText="background-color:rgba(0,0,0,0.5)",!!i.backgroundColor},opacity:function(){return o.style.opacity===""},textshadow:function(){return i.textShadow===""},multiplebgs:function(){i.cssText="background:url(https://),url(https://),red url(https://)";var n=(i.background||"").match(/url/g);return Object.prototype.toString.call(n)==="[object Array]"&&n.length===3},boxshadow:function(){return r("boxShadow")},borderimage:function(){return r("borderImage")},borderradius:function(){return r("borderRadius")},cssreflections:function(){return r("boxReflect")},csstransforms:function(){return r("transform")},csstransitions:function(){return r("transition")},touch:function(){return"ontouchstart"in n},retina:function(){return n.devicePixelRatio>1},fontface:function(){var t=u.browser.name,n=u.browser.version;switch(t){case"ie":return n>=9;case"chrome":return n>=13;case"ff":return n>=6;case"ios":return n>=5;case"android":return!1;case"webkit":return n>=5.1;case"opera":return n>=10;default:return!1}}};for(var e in f)f[e]&&u.feature(e,f[e].call(),!0);u.feature()})(window); 5 | /*! head.load - v1.0.3 */ 6 | (function(n,t){"use strict";function w(){}function u(n,t){if(n){typeof n=="object"&&(n=[].slice.call(n));for(var i=0,r=n.length;i window.innerHeight - rangeY ) { 186 | window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) ); 187 | } 188 | 189 | // Left 190 | if( mouseX < rangeX ) { 191 | window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y ); 192 | } 193 | // Right 194 | else if( mouseX > window.innerWidth - rangeX ) { 195 | window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y ); 196 | } 197 | } 198 | 199 | function getScrollOffset() { 200 | return { 201 | x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset, 202 | y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset 203 | } 204 | } 205 | 206 | return { 207 | /** 208 | * Zooms in on either a rectangle or HTML element. 209 | * 210 | * @param {Object} options 211 | * - element: HTML element to zoom in on 212 | * OR 213 | * - x/y: coordinates in non-transformed space to zoom in on 214 | * - width/height: the portion of the screen to zoom in on 215 | * - scale: can be used instead of width/height to explicitly set scale 216 | */ 217 | to: function( options ) { 218 | 219 | // Due to an implementation limitation we can't zoom in 220 | // to another element without zooming out first 221 | if( level !== 1 ) { 222 | zoom.out(); 223 | } 224 | else { 225 | options.x = options.x || 0; 226 | options.y = options.y || 0; 227 | 228 | // If an element is set, that takes precedence 229 | if( !!options.element ) { 230 | // Space around the zoomed in element to leave on screen 231 | var padding = 20; 232 | var bounds = options.element.getBoundingClientRect(); 233 | 234 | options.x = bounds.left - padding; 235 | options.y = bounds.top - padding; 236 | options.width = bounds.width + ( padding * 2 ); 237 | options.height = bounds.height + ( padding * 2 ); 238 | } 239 | 240 | // If width/height values are set, calculate scale from those values 241 | if( options.width !== undefined && options.height !== undefined ) { 242 | options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 ); 243 | } 244 | 245 | if( options.scale > 1 ) { 246 | options.x *= options.scale; 247 | options.y *= options.scale; 248 | 249 | magnify( options, options.scale ); 250 | 251 | if( options.pan !== false ) { 252 | 253 | // Wait with engaging panning as it may conflict with the 254 | // zoom transition 255 | panEngageTimeout = setTimeout( function() { 256 | panUpdateInterval = setInterval( pan, 1000 / 60 ); 257 | }, 800 ); 258 | 259 | } 260 | } 261 | } 262 | }, 263 | 264 | /** 265 | * Resets the document zoom state to its default. 266 | */ 267 | out: function() { 268 | clearTimeout( panEngageTimeout ); 269 | clearInterval( panUpdateInterval ); 270 | 271 | magnify( { x: 0, y: 0 }, 1 ); 272 | 273 | level = 1; 274 | }, 275 | 276 | // Alias 277 | magnify: function( options ) { this.to( options ) }, 278 | reset: function() { this.out() }, 279 | 280 | zoomLevel: function() { 281 | return level; 282 | } 283 | } 284 | 285 | })(); 286 | 287 | 288 | 289 | -------------------------------------------------------------------------------- /xtictactoe/README.md: -------------------------------------------------------------------------------- 1 | # First Project: Extended TicTacToe 2 | 3 | In this project you should implement the game logic for a simple Tic-Tac-Toe 4 | console game. You will have to work with the `Option`, `Either` and `List` 5 | types from the scala standard library. See the 6 | [scala documentation](https://www.scala-lang.org/api/current/) for 7 | information. 8 | 9 | The project compiles and can be run. However, it is lacking most of the 10 | required functionality. 11 | 12 | ## Task 13 | 14 | You are to implement a fully functional Tic-Tac-Toe game. This should be 15 | achieved by exchanging the stubs in 16 | [Logic.scala](src/main/scala/xtictactoe/Logic.scala) with a proper 17 | implementation. See the comments in that file for details on the functions. 18 | 19 | __There are many possible ways to implement the functions.__ 20 | 21 | We suggest you don't check for diagonals in your initial solution since that can be 22 | a bit tricky. Try to implement a reduced solution first and then figure out how 23 | to tackle that problem. 24 | 25 | It is an important part of the exercise to examine the provided structure 26 | and explore possible solutions with the help of the scala documentation. 27 | 28 | __We encourage you to work in groups.__ 29 | 30 | ## Project Layout 31 | 32 | The project is split into 3 files. The one most relevant to you is 33 | [Logic.scala](src/main/scala/xtictactoe/Logic.scala). It contains stub 34 | implementations for 4 functions. It is your task to understand the role 35 | of each function and to provide a proper implementation. 36 | 37 | Additionally, there are 2 more files: 38 | - [Game.scala](src/main/scala/xtictactoe/Game.scala) contains the game 39 | loop and interaction code 40 | - [Console.scala](src/main/scala/xtictactoe/Console.scala) contains utility 41 | functions to interact with the console 42 | 43 | While you should not change anything in these files, feel free to explore 44 | them. Keep in mind, that they contain advanced concepts not yet covered 45 | in the lectures. 46 | -------------------------------------------------------------------------------- /xtictactoe/src/main/scala/xtictactoe/Console.scala: -------------------------------------------------------------------------------- 1 | package xtictactoe 2 | 3 | import cats.effect.IO 4 | import cats.implicits._ 5 | 6 | import scala.io.StdIn 7 | import scala.util.Try 8 | 9 | object Console { 10 | def println(str: String): IO[Unit] = IO { Predef.println(str) } 11 | 12 | def askValidated[T](msg: String, f: String => Either[String, T]): IO[T] = { 13 | for { 14 | _ <- println(msg) 15 | input = StdIn.readLine() 16 | 17 | _ <- IO(System.exit(0)).whenA(input.isEmpty) 18 | 19 | t <- f(input) match { 20 | case Left(errMsg) => println(errMsg) >> askValidated(msg, f) 21 | case Right(t) => IO(t) 22 | } 23 | } yield t 24 | } 25 | 26 | def askValidatedInt(msg: String, f: Int => Either[String, Int] = i => Right(i)): IO[Int] = { 27 | askValidated(msg, { input => 28 | Try(input.toInt).toEither.left.map(_ => "Please input a number.").flatMap(f) 29 | }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /xtictactoe/src/main/scala/xtictactoe/Game.scala: -------------------------------------------------------------------------------- 1 | package xtictactoe 2 | 3 | import cats.effect.{ExitCode, IO, IOApp} 4 | import cats.implicits._ 5 | 6 | object Game extends IOApp { 7 | def run(args: List[String]): IO[ExitCode] = { 8 | def go(board: Logic.Game, player: Logic.Player): IO[Unit] = { 9 | val winnerOpt = Logic.result(board) 10 | 11 | winnerOpt match { 12 | case Some(Logic.PlayerWon(player)) => 13 | if (player == Logic.Player1) 14 | Console.println("Player 1 won") 15 | else 16 | Console.println("Player 2 won") 17 | 18 | case Some(Logic.Draw) => 19 | Console.println("It's a draw!") 20 | 21 | case None => 22 | def inputLoop: IO[Logic.Game] = { 23 | for { 24 | column <- Console.askValidatedInt("At what column do you want to put your next piece?") 25 | row <- Console.askValidatedInt(s"At what row in column ${column} do you want to put your next piece?") 26 | 27 | newBoard <- Logic.updateBoard(board, player, row, column) match { 28 | case Left(msg) => Console.println(msg) >> inputLoop 29 | case Right(newColumns) => IO(board.copy(board = newColumns)) 30 | } 31 | } yield newBoard 32 | } 33 | 34 | for { 35 | _ <- Console.println("") >> Console.println(player.toString + ", it's your turn.") 36 | newBoard <- inputLoop 37 | _ <- Console.println(Logic.view(newBoard)) 38 | 39 | newPlayer = player match { 40 | case Logic.Player1 => Logic.Player2 41 | case Logic.Player2 => Logic.Player1 42 | } 43 | 44 | _ <- go(newBoard, newPlayer) 45 | } yield () 46 | } 47 | } 48 | 49 | val mainAction = for { 50 | _ <- Console.println("Hey there. Let's play X-XOXO") 51 | size <- Console.askValidatedInt("What's the board size?") 52 | winSize <- Console.askValidatedInt("What's the number of winning pieces?", i => 53 | if (i <= size) Right(i) 54 | else Left("It cannot be bigger than the board size.") 55 | ) 56 | 57 | _ <- go(Logic.initBoard(Some(size), Some(winSize)), Logic.Player1) 58 | } yield () 59 | 60 | mainAction.as(ExitCode.Success) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /xtictactoe/src/main/scala/xtictactoe/Logic.scala: -------------------------------------------------------------------------------- 1 | package xtictactoe 2 | 3 | object Logic { 4 | 5 | /** 6 | * Representation for the players. Can only be `Player1` or `Player2` 7 | */ 8 | sealed trait Player 9 | 10 | case object Player1 extends Player 11 | 12 | case object Player2 extends Player 13 | 14 | /** 15 | * Representation of a single field on the game board. Can be `Empty` or occupied by a player via `Piece(player)` 16 | */ 17 | sealed trait Field 18 | 19 | case object Empty extends Field 20 | 21 | case class Piece(player: Player) extends Field 22 | 23 | /** 24 | * Final result of a game. Either a player won (`PlayerWon(player)`) or it is a `Draw` 25 | */ 26 | sealed trait GameResult 27 | 28 | case class PlayerWon(player: Player) extends GameResult 29 | 30 | case object Draw extends GameResult 31 | 32 | /** 33 | * A column on the game board is represented as a list of `Field`s. 34 | */ 35 | type Column = List[Field] 36 | type Board = List[Column] 37 | 38 | /** 39 | * A `Game` consists of the current state of the board, the size of the board and the number of tiles needed to win. 40 | * 41 | * @param board A list of columns representing the current state of the board 42 | * @param size The size of the board. `board` will consist of `size` lists, each of which has `size` elements 43 | * @param winSize The number of tiles aligned horizontally, vertically or diagonally for a single player. 44 | */ 45 | case class Game(board: Board, size: Int, winSize: Int) 46 | 47 | /** 48 | * This method creates and returns a `Game` instance. The contained board should be of the size in `sizeOpt` or 49 | * set to a reasonable default. Similarly, `winSize` should be taken from the argument or set to a default value 50 | * (that is smaller than the `size`. 51 | * 52 | * @param sizeOpt An option containing the `size` of the board. If `None` a default value will be used. 53 | * @param winSize An option containing the number of winning tiles. If `None` a default value will be used. 54 | * @return A new `Game` with board of size `sizeOpt` (or a default value) 55 | */ 56 | def initBoard(sizeOpt: Option[Int], winSize: Option[Int]): Game = { 57 | // Here we ignore the input and construct a board of size 2 58 | // TODO: change the definition to create a board of the size given in `sizeOpt` 59 | val board = List(List(Empty, Empty), List(Empty, Empty), List(Empty, Empty)) 60 | Game(board, 2, 2) 61 | } 62 | 63 | /** 64 | * This method returns a sensible `String` representation of the board. 65 | * 66 | * @param game The `Game` of which we want to draw the board (i.e. `game.board`) 67 | * @return 68 | */ 69 | def view(game: Game): String = { 70 | // TODO: Construct a nice and readable representation of the board 71 | game.board.transpose.mkString("\n") 72 | } 73 | 74 | /** 75 | * Returns the result of the game if it is finshed or `None` if it is still running. 76 | * 77 | * Possible results are either a won game (by a player) or a draw. 78 | * 79 | * @param game The game we want to get a result for 80 | * @return Either the game's result (won or draw) or `None` if the game is not finished yet. 81 | */ 82 | def result(game: Game): Option[GameResult] = { 83 | // TODO: This game will never stop. This should be fixed. Make sure to return the proper result. 84 | // Hints: 85 | // - It helps to break the task down. As a first step, check if a player has enough sequential pieces in a column. 86 | // - It is often better (i.e. more readable and maintainable) to not use indices. Try to use combinators (i.e. methods 87 | // of the `List` class). See https://www.scala-lang.org/api/2.12.3/scala/collection/immutable/List.html . 88 | // - Don't worry about performance! 89 | // - It suffices to return `Draw` only if no more moves are possible. 90 | None 91 | } 92 | 93 | /** 94 | * Try to compute the game's board state with a piece for `player` set on the field specified by `row` and `column` 95 | * and return the new board (in a `Right`). 96 | * 97 | * There are several cases in which this method cannot provide a new state: 98 | * - The row or column value are out of bounds 99 | * - The field is already occupied. 100 | * In any such case, the function will not return the new state but instead provide a helpful error message in a `Left`. 101 | * 102 | * @param game The current state of the game we want to update. 103 | * @param player The active player (making the move) 104 | * @param row The row of the new piece 105 | * @param column The column of the new piece. 106 | * @return An `Either` containing either an error message in `Left` or the new board (list of columns) in `Right`. 107 | */ 108 | def updateBoard(game: Game, player: Player, row: Int, column: Int): Either[String, Board] = { 109 | // We return the original board here 110 | // TODO: Implement! Create an updated board or produce an error message. 111 | Right(game.board) 112 | } 113 | } 114 | --------------------------------------------------------------------------------