├── .bsp └── sbt.json ├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── misc.xml ├── modules.xml ├── modules │ ├── udemy-scala-beginners-build.iml │ └── udemy-scala-beginners.iml ├── sbt.xml ├── scala_compiler.xml ├── scala_settings.xml └── vcs.xml ├── README.md ├── build.sbt ├── project └── build.properties └── src └── main └── scala ├── exercises ├── Maybe.scala └── MyList.scala ├── lectures ├── part1basics │ ├── CBNvsCBV.scala │ ├── DefaultArgs.scala │ ├── Expressions.scala │ ├── Functions.scala │ ├── Recursion.scala │ ├── StringOps.scala │ └── ValuesVariablesTypes.scala ├── part2oop │ ├── AbstractDataTypes.scala │ ├── AnonymousClasses.scala │ ├── CaseClasses.scala │ ├── Enums.scala │ ├── Exceptions.scala │ ├── Generics.scala │ ├── Inheritance.scala │ ├── MethodNotations.scala │ ├── OOBasics.scala │ ├── Objects.scala │ ├── PackagingAndImports.scala │ └── package.scala ├── part3fp │ ├── AnonymousFunctions.scala │ ├── HOFsCurries.scala │ ├── HandlingFailure.scala │ ├── MapFlatmapFilterFor.scala │ ├── Options.scala │ ├── Sequences.scala │ ├── TuplesAndMaps.scala │ └── WhatsAFunction.scala └── part4power │ ├── AllThePatterns.scala │ ├── BracelessSyntax.scala │ ├── PatternMatching.scala │ └── PatternsEverywhere.scala └── playground ├── Cinderella.scala ├── JavaPlayground.java ├── PrinceCharming.scala └── ScalaPlayground.scala /.bsp/sbt.json: -------------------------------------------------------------------------------- 1 | {"name":"sbt","version":"1.9.9","bspVersion":"2.1.0-M1","languages":["scala"],"argv":["/Users/daniel/Library/Java/JavaVirtualMachines/temurin-17.0.9/Contents/Home/bin/java","-Xms100m","-Xmx100m","-classpath","/Users/daniel/Library/Application Support/JetBrains/IdeaIC2023.3/plugins/Scala/launcher/sbt-launch.jar","-Dsbt.script=/Users/daniel/Library/Application%20Support/Coursier/bin/sbt","xsbt.boot.Boot","-bsp"]} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/scala,java,intellij,sbt 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=scala,java,intellij,sbt 4 | 5 | ### Intellij ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # User-specific stuff 10 | .idea/**/workspace.xml 11 | .idea/**/tasks.xml 12 | .idea/**/usage.statistics.xml 13 | .idea/**/dictionaries 14 | .idea/**/shelf 15 | 16 | # AWS User-specific 17 | .idea/**/aws.xml 18 | 19 | # Generated files 20 | .idea/**/contentModel.xml 21 | 22 | # Sensitive or high-churn files 23 | .idea/**/dataSources/ 24 | .idea/**/dataSources.ids 25 | .idea/**/dataSources.local.xml 26 | .idea/**/sqlDataSources.xml 27 | .idea/**/dynamic.xml 28 | .idea/**/uiDesigner.xml 29 | .idea/**/dbnavigator.xml 30 | 31 | # Gradle 32 | .idea/**/gradle.xml 33 | .idea/**/libraries 34 | 35 | # Gradle and Maven with auto-import 36 | # When using Gradle or Maven with auto-import, you should exclude module files, 37 | # since they will be recreated, and may cause churn. Uncomment if using 38 | # auto-import. 39 | # .idea/artifacts 40 | # .idea/compiler.xml 41 | # .idea/jarRepositories.xml 42 | # .idea/modules.xml 43 | # .idea/*.iml 44 | # .idea/modules 45 | # *.iml 46 | # *.ipr 47 | 48 | # CMake 49 | cmake-build-*/ 50 | 51 | # Mongo Explorer plugin 52 | .idea/**/mongoSettings.xml 53 | 54 | # File-based project format 55 | *.iws 56 | 57 | # IntelliJ 58 | out/ 59 | 60 | # mpeltonen/sbt-idea plugin 61 | .idea_modules/ 62 | 63 | # JIRA plugin 64 | atlassian-ide-plugin.xml 65 | 66 | # Cursive Clojure plugin 67 | .idea/replstate.xml 68 | 69 | # Crashlytics plugin (for Android Studio and IntelliJ) 70 | com_crashlytics_export_strings.xml 71 | crashlytics.properties 72 | crashlytics-build.properties 73 | fabric.properties 74 | 75 | # Editor-based Rest Client 76 | .idea/httpRequests 77 | 78 | # Android studio 3.1+ serialized cache file 79 | .idea/caches/build_file_checksums.ser 80 | 81 | ### Intellij Patch ### 82 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 83 | 84 | # *.iml 85 | # modules.xml 86 | # .idea/misc.xml 87 | # *.ipr 88 | 89 | # Sonarlint plugin 90 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 91 | .idea/**/sonarlint/ 92 | 93 | # SonarQube Plugin 94 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 95 | .idea/**/sonarIssues.xml 96 | 97 | # Markdown Navigator plugin 98 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 99 | .idea/**/markdown-navigator.xml 100 | .idea/**/markdown-navigator-enh.xml 101 | .idea/**/markdown-navigator/ 102 | 103 | # Cache file creation bug 104 | # See https://youtrack.jetbrains.com/issue/JBR-2257 105 | .idea/$CACHE_FILE$ 106 | 107 | # CodeStream plugin 108 | # https://plugins.jetbrains.com/plugin/12206-codestream 109 | .idea/codestream.xml 110 | 111 | ### Java ### 112 | # Compiled class file 113 | *.class 114 | 115 | # Log file 116 | *.log 117 | 118 | # BlueJ files 119 | *.ctxt 120 | 121 | # Mobile Tools for Java (J2ME) 122 | .mtj.tmp/ 123 | 124 | # Package Files # 125 | *.jar 126 | *.war 127 | *.nar 128 | *.ear 129 | *.zip 130 | *.tar.gz 131 | *.rar 132 | 133 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 134 | hs_err_pid* 135 | 136 | ### SBT ### 137 | # Simple Build Tool 138 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 139 | 140 | dist/* 141 | target/ 142 | lib_managed/ 143 | src_managed/ 144 | project/boot/ 145 | project/plugins/project/ 146 | .history 147 | .cache 148 | .lib/ 149 | 150 | ### Scala ### 151 | 152 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 153 | 154 | # End of https://www.toptal.com/developers/gitignore/api/scala,java,intellij,sbt 155 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules/udemy-scala-beginners-build.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 115 | -------------------------------------------------------------------------------- /.idea/modules/udemy-scala-beginners.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/sbt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/scala_compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/scala_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## The official repository for the Scala beginners course on Udemy 3 | 4 | This repository contains the code we wrote during [Rock the JVM's Scala beginners course](https://www.udemy.com/rock-the-jvm-scala-for-beginners) on Udemy. Unless explicitly mentioned, the code in this repository is exactly what was caught on camera. 5 | 6 | How to install: 7 | - either clone the repo or download as zip 8 | - open with IntelliJ as it's a simple IDEA project 9 | 10 | If you have changes to suggest to this repo, either 11 | - submit a GitHub issue 12 | - tell me in the course Q/A forum 13 | - submit a pull request! 14 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "udemy-scala-beginners" 2 | 3 | version := "0.1" 4 | 5 | scalaVersion := "3.3.1" 6 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 1.9.9 -------------------------------------------------------------------------------- /src/main/scala/exercises/Maybe.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | /** 4 | * Created by Daniel. 5 | */ 6 | abstract class Maybe[+T] { 7 | 8 | def map[B](f: T => B): Maybe[B] 9 | def flatMap[B](f: T => Maybe[B]): Maybe[B] 10 | def filter(p: T => Boolean): Maybe[T] 11 | } 12 | 13 | case object MaybeNot extends Maybe[Nothing] { 14 | def map[B](f: Nothing => B): Maybe[B] = MaybeNot 15 | def flatMap[B](f: Nothing => Maybe[B]): Maybe[B] = MaybeNot 16 | def filter(p: Nothing => Boolean): Maybe[Nothing] = MaybeNot 17 | } 18 | 19 | case class Just[+T](value: T) extends Maybe[T] { 20 | 21 | def map[B](f: T => B): Maybe[B] = Just(f(value)) 22 | def flatMap[B](f: T => Maybe[B]): Maybe[B] = f(value) 23 | def filter(p: T => Boolean): Maybe[T] = 24 | if (p(value)) this 25 | else MaybeNot 26 | 27 | } 28 | 29 | object MaybeTest extends App { 30 | val just3 = Just(3) 31 | println(just3) 32 | println(just3.map(_ * 2)) 33 | println(just3.flatMap(x => Just(x % 2 == 0))) 34 | println(just3.filter(_ % 2 == 0)) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/exercises/MyList.scala: -------------------------------------------------------------------------------- 1 | package exercises 2 | 3 | abstract class MyList[+A] { 4 | 5 | /* 6 | head = first element of the list 7 | tail = remainder of the list 8 | isEmpty = is this list empty 9 | add(int) => new list with this element added 10 | toString => a string representation of the list 11 | */ 12 | 13 | def head: A 14 | def tail: MyList[A] 15 | def isEmpty: Boolean 16 | def add[B >: A](element: B): MyList[B] 17 | def printElements: String 18 | // polymorphic call 19 | override def toString: String = "[" + printElements + "]" 20 | 21 | // higher-order functions 22 | def map[B](transformer: A => B): MyList[B] 23 | def flatMap[B](transformer: A => MyList[B]): MyList[B] 24 | def filter(predicate: A => Boolean): MyList[A] 25 | 26 | // concatenation 27 | def ++[B >: A](list: MyList[B]): MyList[B] 28 | 29 | // hofs 30 | def foreach(f: A => Unit): Unit 31 | def sort(compare: (A, A) => Int): MyList[A] 32 | def zipWith[B, C](list: MyList[B], zip:(A, B) => C): MyList[C] 33 | def fold[B](start: B)(operator: (B, A) => B): B 34 | 35 | def reverse: MyList[A] = { 36 | def reverseTailrec(input: MyList[A], result: MyList[A]): MyList[A] = 37 | if (input.isEmpty) result 38 | else reverseTailrec(input.tail, Cons(input.head, result)) 39 | 40 | reverseTailrec(this, Empty) 41 | } 42 | } 43 | 44 | case object Empty extends MyList[Nothing] { 45 | def head: Nothing = throw new NoSuchElementException 46 | def tail: MyList[Nothing] = throw new NoSuchElementException 47 | def isEmpty: Boolean = true 48 | def add[B >: Nothing](element: B): MyList[B] = new Cons(element, Empty) 49 | def printElements: String = "" 50 | 51 | def map[B](transformer: Nothing => B): MyList[B] = Empty 52 | def flatMap[B](transformer: Nothing => MyList[B]): MyList[B] = Empty 53 | def filter(predicate: Nothing => Boolean): MyList[Nothing] = Empty 54 | 55 | def ++[B >: Nothing](list: MyList[B]): MyList[B] = list 56 | 57 | // hofs 58 | def foreach(f: Nothing => Unit): Unit = () 59 | def sort(compare: (Nothing, Nothing) => Int) = Empty 60 | def zipWith[B, C](list: MyList[B], zip: (Nothing, B) => C): MyList[C] = 61 | if (!list.isEmpty) throw new RuntimeException("Lists do not have the same length") 62 | else Empty 63 | def fold[B](start: B)(operator: (B, Nothing) => B): B = start 64 | 65 | } 66 | 67 | case class Cons[+A](h: A, t: MyList[A]) extends MyList[A] { 68 | def head: A = h 69 | def tail: MyList[A] = t 70 | def isEmpty: Boolean = false 71 | def add[B >: A](element: B): MyList[B] = new Cons(element, this) 72 | def printElements: String = 73 | if(t.isEmpty) "" + h 74 | else s"$h ${t.printElements}" 75 | 76 | /* 77 | [1,2,3].filter(n % 2 == 0) = 78 | [2,3].filter(n % 2 == 0) = 79 | = new Cons(2, [3].filter(n % 2 == 0)) 80 | = new Cons(2, Empty.filter(n % 2 == 0)) 81 | = new Cons(2, Empty) 82 | */ 83 | def filter(predicate: A => Boolean): MyList[A] = 84 | if (predicate(h)) new Cons(h, t.filter(predicate)) 85 | else t.filter(predicate) 86 | 87 | /* 88 | [1,2,3].map(n * 2) 89 | = new Cons(2, [2,3].map(n * 2)) 90 | = new Cons(2, new Cons(4, [3].map(n * 2))) 91 | = new Cons(2, new Cons(4, new Cons(6, Empty.map(n * 2)))) 92 | = new Cons(2, new Cons(4, new Cons(6, Empty)))) 93 | */ 94 | def map[B](transformer: A => B): MyList[B] = 95 | new Cons(transformer(h), t.map(transformer)) 96 | 97 | /* 98 | [1,2] ++ [3,4,5] 99 | = new Cons(1, [2] ++ [3,4,5]) 100 | = new Cons(1, new Cons(2, Empty ++ [3,4,5])) 101 | = new Cons(1, new Cons(2, new Cons(3, new Cons(4, new Cons(5))))) 102 | */ 103 | def ++[B >: A](list: MyList[B]): MyList[B] = new Cons(h, t ++ list) 104 | /* 105 | [1,2].flatMap(n => [n, n+1]) 106 | = [1,2] ++ [2].flatMap(n => [n, n+1]) 107 | = [1,2] ++ [2,3] ++ Empty.flatMap(n => [n, n+1]) 108 | = [1,2] ++ [2,3] ++ Empty 109 | = [1,2,2,3] 110 | */ 111 | def flatMap[B](transformer: A => MyList[B]): MyList[B] = 112 | transformer(h) ++ t.flatMap(transformer) 113 | 114 | // hofs 115 | def foreach(f: A => Unit): Unit = { 116 | f(h) 117 | t.foreach(f) 118 | } 119 | 120 | def sort(compare: (A, A) => Int): MyList[A] = { 121 | def insert(x: A, sortedList: MyList[A]): MyList[A] = 122 | if (sortedList.isEmpty) new Cons(x, Empty) 123 | else if (compare(x, sortedList.head) <= 0) new Cons(x, sortedList) 124 | else new Cons(sortedList.head, insert(x, sortedList.tail)) 125 | 126 | val sortedTail = t.sort(compare) 127 | insert(h, sortedTail) 128 | } 129 | 130 | def zipWith[B, C](list: MyList[B], zip: (A, B) => C): MyList[C] = 131 | if (list.isEmpty) throw new RuntimeException("Lists do not have the same length") 132 | else new Cons(zip(h, list.head), t.zipWith(list.tail, zip)) 133 | 134 | /* 135 | [1,2,3].fold(0)(+) = 136 | = [2,3].fold(1)(+) = 137 | = [3].fold(3)(+) = 138 | = [].fold(6)(+) 139 | = 6 140 | */ 141 | def fold[B](start: B)(operator: (B, A) => B): B = 142 | t.fold(operator(start, h))(operator) 143 | 144 | } 145 | 146 | object ListTest extends App { 147 | val listOfIntegers: MyList[Int] = new Cons(1, new Cons(2, new Cons(3, Empty))) 148 | val cloneListOfIntegers: MyList[Int] = new Cons(1, new Cons(2, new Cons(3, Empty))) 149 | val anotherListOfIntegers: MyList[Int] = new Cons(4, new Cons(5, Empty)) 150 | val listOfStrings: MyList[String] = new Cons("Hello", new Cons("Scala", Empty)) 151 | 152 | println(listOfIntegers.toString) 153 | println(listOfStrings.toString) 154 | 155 | println(listOfIntegers.map(_ * 2).toString) 156 | 157 | println(listOfIntegers.filter(_ % 2 == 0).toString) 158 | 159 | println((listOfIntegers ++ anotherListOfIntegers).toString) 160 | println(listOfIntegers.flatMap(elem => new Cons(elem, new Cons(elem + 1, Empty))).toString) 161 | 162 | println(cloneListOfIntegers == listOfIntegers) 163 | 164 | listOfIntegers.foreach(println) 165 | println(listOfIntegers.sort((x, y) => y - x)) 166 | println(anotherListOfIntegers.zipWith[String, String](listOfStrings, _ + "-" + _)) 167 | println(listOfIntegers.fold(0)(_ + _)) 168 | 169 | // for comprehensions 170 | val combinations = for { 171 | n <- listOfIntegers 172 | string <- listOfStrings 173 | } yield n + "-" + string 174 | println(combinations) 175 | 176 | def sort(list: MyList[Int]): MyList[Int] = { 177 | def insertSort(sortedList: MyList[Int], elem: Int, lessThanElement: MyList[Int] = Empty): MyList[Int] = 178 | if (sortedList.isEmpty || elem < sortedList.head) lessThanElement.reverse ++ Cons(elem, sortedList) 179 | else insertSort(sortedList.tail, elem, Cons(sortedList.head, lessThanElement)) 180 | 181 | list.fold[MyList[Int]](Empty)((sorted, elem) => insertSort(sorted, elem)) 182 | } 183 | 184 | println(sort(Cons(4, Cons(2, Cons(5, Cons(1, Cons(3, Empty))))))) 185 | } 186 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/CBNvsCBV.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | object CBNvsCBV extends App { 4 | 5 | def calledByValue(x: Long): Unit = { 6 | println("by value: " + x) 7 | println("by value: " + x) 8 | } 9 | 10 | def calledByName(x: => Long): Unit = { 11 | println("by name: " + x) 12 | println("by name: " + x) 13 | } 14 | 15 | calledByValue(1257387745764245L) 16 | calledByName(System.nanoTime()) 17 | 18 | def infinite(): Int = 1 + infinite() 19 | def printFirst(x: Int, y: => Int) = println(x) 20 | 21 | // printFirst(infinite(), 34) // stack overflow 22 | printFirst(34, infinite()) 23 | } 24 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/DefaultArgs.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | object DefaultArgs extends App { 4 | 5 | def trFact(n: Int, acc: Int = 1): Int = 6 | if (n <= 1) acc 7 | else trFact(n-1, n*acc) 8 | 9 | val fact10 = trFact(10, 2) 10 | 11 | def savePicture(format: String = "jpg", width: Int = 1920, height: Int = 1080): Unit = println("saving picture") 12 | savePicture(width = 800) 13 | 14 | /* 15 | 1. pass in every leading argument 16 | 2. name the arguments 17 | */ 18 | 19 | savePicture(height = 600, width = 800, format = "bmp") 20 | } 21 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/Expressions.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | object Expressions extends App { 4 | 5 | val x = 1 + 2 // EXPRESSION 6 | println(x) 7 | 8 | println(2 + 3 * 4) 9 | // + - * / & | ^ << >> >>> (right shift with zero extension) 10 | 11 | println(1 == x) 12 | // == != > >= < <= 13 | 14 | println(!(1 == x)) 15 | // ! && || 16 | 17 | var aVariable = 2 18 | aVariable += 3 // also works with -= *= /= ..... side effects 19 | println(aVariable) 20 | 21 | // Instructions (DO) vs Expressions (VALUE) 22 | 23 | // IF expression 24 | val aCondition = true 25 | val aConditionedValue = if(aCondition) 5 else 3 // IF EXPRESSION 26 | println(aConditionedValue) 27 | println(if(aCondition) 5 else 3) 28 | println(1 + 3) 29 | 30 | var i = 0 31 | val aWhile = while (i < 10) { 32 | println(i) 33 | i += 1 34 | } 35 | 36 | // NEVER WRITE THIS AGAIN. 37 | 38 | // EVERYTHING in Scala is an Expression! 39 | 40 | val aWeirdValue = (aVariable = 3) // Unit === void 41 | println(aWeirdValue) 42 | 43 | // side effects: println(), whiles, reassigning 44 | 45 | // Code blocks 46 | 47 | val aCodeBlock = { 48 | val y = 2 49 | val z = y + 1 50 | 51 | if (z > 2) "hello" else "goodbye" 52 | } 53 | 54 | // 1. difference between "hello world" vs println("hello world")? 55 | // 2. 56 | 57 | val someValue = { 58 | 2 < 3 59 | } 60 | println(someValue) 61 | 62 | val someOtherValue = { 63 | if(someValue) 239 else 986 64 | 42 65 | } 66 | println(someOtherValue) 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/Functions.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | object Functions extends App { 4 | 5 | def aFunction(a: String, b: Int): String = { 6 | a + " " + b 7 | } 8 | 9 | println(aFunction("hello", 3)) 10 | 11 | def aParameterlessFunction(): Int = 42 12 | println(aParameterlessFunction()) 13 | // println(aParameterlessFunction) // only works in Scala 2 - parameterless functions are invoked without parentheses 14 | 15 | def aRepeatedFunction(aString: String, n: Int): String = { 16 | if (n == 1) aString 17 | else aString + aRepeatedFunction(aString, n-1) 18 | } 19 | 20 | println(aRepeatedFunction("hello",3)) 21 | 22 | // WHEN YOU NEED LOOPS, USE RECURSION. 23 | 24 | def aFunctionWithSideEffects(aString: String): Unit = println(aString) 25 | 26 | def aBigFunction(n: Int): Int = { 27 | def aSmallerFunction(a: Int, b: Int): Int = a + b 28 | 29 | aSmallerFunction(n, n-1) 30 | } 31 | 32 | /* 33 | Exercises: 34 | 1. A greeting function (name, age) => "Hi, my name is $name and I am $age years old." 35 | 2. Factorial function 1 * 2 * 3 * .. * n 36 | 3. A Fibonacci function 37 | f(1) = 1 38 | f(2) = 1 39 | f(n) = f(n - 1) + f(n - 2) 40 | 4. Tests if a number is prime. 41 | */ 42 | 43 | def greetingForKids(name: String, age: Int): String = 44 | "Hi, my name is " + name + " and I am " + age + " years old." 45 | println(greetingForKids("David", 12)) 46 | 47 | def factorial(n: Int): Int = 48 | if (n <= 0) 1 49 | else n * factorial(n-1) 50 | 51 | println(factorial(5)) 52 | 53 | def fibonacci(n: Int): Int = 54 | if (n <= 2) 1 55 | else fibonacci(n-1) + fibonacci(n-2) 56 | 57 | // 1 1 2 3 5 8 13 21 58 | println(fibonacci(8)) 59 | 60 | def isPrime(n: Int): Boolean = { 61 | def isPrimeUntil(t: Int): Boolean = 62 | if (t <= 1) true 63 | else n % t != 0 && isPrimeUntil(t-1) 64 | 65 | isPrimeUntil(n / 2) 66 | } 67 | println(isPrime(37)) 68 | println(isPrime(2003)) 69 | println(isPrime(37 * 17)) 70 | } 71 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/Recursion.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | import scala.annotation.tailrec 4 | 5 | object Recursion extends App { 6 | 7 | def factorial(n: Int): Int = 8 | if (n <= 1) 1 9 | else { 10 | println("Computing factorial of " + n + " - I first need factorial of " + (n-1)) 11 | val result = n * factorial(n-1) 12 | println("Computed factorial of " + n) 13 | 14 | result 15 | } 16 | 17 | println(factorial(10)) 18 | // println(factorial(5000)) // stack overflow! 19 | 20 | def anotherFactorial(n: Int): BigInt = { 21 | @tailrec 22 | def factHelper(x: Int, accumulator: BigInt): BigInt = 23 | if (x <= 1) accumulator 24 | else factHelper(x - 1, x * accumulator) // TAIL RECURSION = use recursive call as the LAST expression 25 | 26 | factHelper(n, 1) 27 | } 28 | 29 | /* 30 | Breakdown: 31 | 32 | anotherFactorial(10) = factHelper(10, 1) 33 | = factHelper(9, 10 * 1) 34 | = factHelper(8, 9 * 10 * 1) 35 | = factHelper(7, 8 * 9 * 10 * 1) 36 | = ... 37 | = factHelper(2, 3 * 4 * ... * 10 * 1) 38 | = factHelper(1, 1 * 2 * 3 * 4 * ... * 10) 39 | = 1 * 2 * 3 * 4 * ... * 10 40 | */ 41 | 42 | println(anotherFactorial(20000)) 43 | 44 | // WHEN YOU NEED LOOPS, USE _TAIL_ RECURSION. 45 | 46 | /* 47 | Exercises: 48 | 1. Concatenate a string n times 49 | 2. IsPrime function tail recursive 50 | 3. Fibonacci function, tail recursive. 51 | */ 52 | 53 | @tailrec 54 | def concatenateTailrec(aString: String, n: Int, accumulator: String): String = 55 | if (n <= 0) accumulator 56 | else concatenateTailrec(aString, n-1, aString + accumulator) 57 | 58 | println(concatenateTailrec("hello", 3, "")) 59 | 60 | def isPrime(n: Int): Boolean = { 61 | @tailrec 62 | def isPrimeTailrec(t: Int, isStillPrime: Boolean): Boolean = 63 | if (!isStillPrime) false 64 | else if (t <= 1) true 65 | else isPrimeTailrec(t - 1, n % t != 0 && isStillPrime) 66 | 67 | isPrimeTailrec(n / 2, true) 68 | } 69 | 70 | println(isPrime(2003)) 71 | println(isPrime(629)) 72 | 73 | def fibonacci(n: Int): Int = { 74 | def fiboTailrec(i: Int, last: Int, nextToLast: Int): Int = 75 | if(i >= n) last 76 | else fiboTailrec(i + 1, last + nextToLast, last) 77 | 78 | if (n <= 2) 1 79 | else fiboTailrec(2, 1, 1) 80 | } 81 | 82 | println(fibonacci(8)) // 1 1 2 3 5 8 13, 21 83 | } 84 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/StringOps.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | object StringOps extends App { 4 | 5 | val str: String = "Hello, I am learning Scala" 6 | 7 | println(str.charAt(2)) 8 | println(str.substring(7, 11)) 9 | println(str.split(" ").toList) 10 | println(str.startsWith("Hello")) 11 | println(str.replace(" ", "-")) 12 | println(str.toLowerCase()) 13 | println(str.length) 14 | 15 | val aNumberString = "2" 16 | val aNumber = aNumberString.toInt 17 | println('a' +: aNumberString :+ 'z') 18 | println(str.reverse) 19 | println(str.take(2)) 20 | 21 | // Scala-specific: String interpolators. 22 | 23 | // S-interpolators 24 | val name = "David" 25 | val age = 12 26 | val greeting = s"Hello, my name is $name and I am $age years old" 27 | val anotherGreeting = s"Hello, my name is $name and I will be turning ${age + 1} years old." 28 | println(anotherGreeting) 29 | 30 | // F-interpolators 31 | val speed = 1.2f 32 | val myth = f"$name can eat $speed%2.2f burgers per minute" 33 | println(myth) 34 | 35 | // raw-interpolator 36 | println(raw"This is a \n newline") 37 | val escaped = "This is a \n newline" 38 | println(raw"$escaped") 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part1basics/ValuesVariablesTypes.scala: -------------------------------------------------------------------------------- 1 | package lectures.part1basics 2 | 3 | object ValuesVariablesTypes extends App { 4 | 5 | val x: Int = 42 6 | println(x) 7 | 8 | // VALS ARE IMMUTABLE 9 | 10 | // COMPILER can infer types 11 | 12 | val aString: String = "hello" 13 | val anotherString = "goodbye" 14 | 15 | val aBoolean: Boolean = false 16 | val aChar: Char = 'a' 17 | val anInt: Int = x 18 | val aShort: Short = 4613 19 | val aLong: Long = 5273985273895237L 20 | val aFloat: Float = 2.0f 21 | val aDouble: Double = 3.14 22 | 23 | // variables 24 | var aVariable: Int = 4 25 | 26 | aVariable = 5 // side effects 27 | } 28 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/AbstractDataTypes.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object AbstractDataTypes extends App { 4 | 5 | // abstract 6 | abstract class Animal { 7 | val creatureType: String = "wild" 8 | def eat: Unit 9 | } 10 | 11 | class Dog extends Animal { 12 | override val creatureType: String = "Canine" 13 | def eat: Unit = println("crunch crunch") 14 | } 15 | 16 | // traits 17 | trait Carnivore { 18 | def eat(animal: Animal): Unit 19 | val preferredMeal: String = "fresh meat" 20 | } 21 | 22 | trait ColdBlooded 23 | class Crocodile extends Animal with Carnivore with ColdBlooded { 24 | override val creatureType: String = "croc" 25 | def eat: Unit = println("nomnomnom") 26 | def eat(animal: Animal): Unit = println(s"I'm a croc and I'm eating ${animal.creatureType}") 27 | } 28 | 29 | val dog = new Dog 30 | val croc = new Crocodile 31 | croc.eat(dog) 32 | 33 | // traits vs abstract classes 34 | // 1 - traits do not have constructor parameters 35 | // 2 - multiple traits may be inherited by the same class 36 | // 3 - traits = behavior, abstract class = "thing" 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/AnonymousClasses.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object AnonymousClasses extends App { 4 | 5 | abstract class Animal { 6 | def eat: Unit 7 | } 8 | 9 | // anonymous class 10 | val funnyAnimal: Animal = new Animal { 11 | override def eat: Unit = println("ahahahahahaah") 12 | } 13 | /* 14 | equivalent with 15 | 16 | class AnonymousClasses$$anon$1 extends Animal { 17 | override def eat: Unit = println("ahahahahahaah") 18 | } 19 | val funnyAnimal: Animal = new AnonymousClasses$$anon$1 20 | */ 21 | 22 | println(funnyAnimal.getClass) 23 | 24 | class Person(name: String) { 25 | def sayHi: Unit = println(s"Hi, my name is $name, how can I help?") 26 | } 27 | 28 | val jim = new Person("Jim") { 29 | override def sayHi: Unit = println(s"Hi, my name is Jim, how can I be of service?") 30 | } 31 | 32 | /* 33 | Exercises: 34 | 1. Generic trait MyPredicate[-T] with a little method test(T) => Boolean 35 | 2. Generic trait MyTransformer[-A, B] with a method transform(A) => B 36 | 3. MyList: 37 | - map(transformer) => MyList 38 | - filter(predicate) => MyList 39 | - flatMap(transformer from A to MyList[B]) => MyList[B] 40 | 41 | class EvenPredicate extends MyPredicate[Int] 42 | class StringToIntTransformer extends MyTransformer[String, Int] 43 | 44 | [1,2,3].map(n * 2) = [2,4,6] 45 | [1,2,3,4].filter(n % 2) = [2,4] 46 | [1,2,3].flatMap(n => [n, n+1]) => [1,2,2,3,3,4] 47 | */ 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/CaseClasses.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object CaseClasses extends App { 4 | 5 | /* 6 | equals, hashCode, toString 7 | */ 8 | 9 | case class Person(name: String, age: Int) 10 | 11 | // 1. class parameters are fields 12 | val jim = new Person("Jim", 34) 13 | println(jim.name) 14 | 15 | // 2. sensible toString 16 | // println(instance) = println(instance.toString) // syntactic sugar 17 | println(jim) 18 | 19 | // 3. equals and hashCode implemented OOTB 20 | val jim2 = new Person("Jim", 34) 21 | println(jim == jim2) 22 | 23 | // 4. CCs have handy copy method 24 | val jim3 = jim.copy(age = 45) 25 | println(jim3) 26 | 27 | // 5. CCs have companion objects 28 | val thePerson = Person 29 | val mary = Person("Mary", 23) 30 | 31 | // 6. CCs are serializable 32 | // Akka 33 | 34 | // 7. CCs have extractor patterns = CCs can be used in PATTERN MATCHING 35 | 36 | case object UnitedKingdom { 37 | def name: String = "The UK of GB and NI" 38 | } 39 | 40 | /* 41 | Expand MyList - use case classes and case objects 42 | */ 43 | } 44 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/Enums.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object Enums { 4 | 5 | enum Permissions { 6 | case READ, WRITE, EXECUTE, NONE 7 | 8 | // add fields/methods 9 | def openDocument(): Unit = 10 | if (this == READ) println("opening document...") 11 | else println("reading not allowed.") 12 | } 13 | 14 | val somePermissions: Permissions = Permissions.READ 15 | 16 | // constructor args 17 | enum PermissionsWithBits(bits: Int) { 18 | case READ extends PermissionsWithBits(4) // 100 19 | case WRITE extends PermissionsWithBits(2) // 010 20 | case EXECUTE extends PermissionsWithBits(1) // 001 21 | case NONE extends PermissionsWithBits(0) // 000 22 | } 23 | 24 | object PermissionsWithBits { 25 | def fromBits(bits: Int): PermissionsWithBits = // whatever 26 | PermissionsWithBits.NONE 27 | } 28 | 29 | // standard API 30 | val somePermissionsOrdinal = somePermissions.ordinal 31 | val allPermissions = PermissionsWithBits.values // array of all possible values of the enum 32 | val readPermission: Permissions = Permissions.valueOf("READ") // Permissions.READ 33 | 34 | def main(args: Array[String]): Unit = { 35 | somePermissions.openDocument() 36 | println(somePermissionsOrdinal) 37 | println(Permissions.WRITE.toString) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/Exceptions.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object Exceptions extends App { 4 | 5 | val x: String = null 6 | // println(x.length) 7 | // this ^^ will crash with a NPE 8 | 9 | // 1. throwing exceptions 10 | // val aWeirdValue: String = throw new NullPointerException // also crashes 11 | 12 | // throwable classes extend the Throwable class. 13 | // Exception and Error are the major Throwable subtypes 14 | 15 | // 2. how to catch exceptions 16 | def getInt(withExceptions: Boolean): Int = 17 | if (withExceptions) throw new RuntimeException("No int for you!") 18 | else 42 19 | 20 | val potentialFail = try { 21 | // code that might throw 22 | getInt(false) 23 | } catch { 24 | case e: RuntimeException => 43 25 | } finally { 26 | // code that will get executed NO MATTER WHAT 27 | // optional 28 | // does not influence the return type of this expression 29 | // use finally only for side effects 30 | println("finally") 31 | } 32 | 33 | println(potentialFail) 34 | 35 | // 3. how to define your own exceptions 36 | class MyException extends Exception 37 | val exception = new MyException 38 | 39 | // throw exception 40 | 41 | /* 42 | 1. Crash your program with an OutOfMemoryError 43 | 2. Crash with SOError 44 | 3. PocketCalculator 45 | - add(x,y) 46 | - subtract(x,y) 47 | - multiply(x,y) 48 | - divide(x,y) 49 | 50 | Throw 51 | - OverflowException if add(x,y) exceeds Int.MAX_VALUE 52 | - UnderflowException if subtract(x,y) exceeds Int.MIN_VALUE 53 | - MathCalculationException for division by 0 54 | */ 55 | // OOM 56 | // val array = Array.ofDim(Int.MaxValue) 57 | 58 | // SO 59 | // def infinite: Int = 1 + infinite 60 | // val noLimit = infinite 61 | 62 | class OverflowException extends RuntimeException 63 | class UnderflowException extends RuntimeException 64 | class MathCalculationException extends RuntimeException("Division by 0") 65 | 66 | object PocketCalculator { 67 | def add(x: Int, y: Int) = { 68 | val result = x + y 69 | 70 | if (x > 0 && y > 0 && result < 0) throw new OverflowException 71 | else if (x < 0 && y < 0 && result > 0) throw new UnderflowException 72 | else result 73 | } 74 | 75 | def subtract(x: Int, y: Int) = { 76 | val result = x - y 77 | if (x > 0 && y < 0 && result < 0) throw new OverflowException 78 | else if (x < 0 && y > 0 && result > 0) throw new UnderflowException 79 | else result 80 | } 81 | 82 | def multiply(x: Int, y: Int) = { 83 | val result = x * y 84 | if (x > 0 && y > 0 && result < 0) throw new OverflowException 85 | else if (x < 0 && y < 0 && result < 0) throw new OverflowException 86 | else if (x > 0 && y < 0 && result > 0) throw new UnderflowException 87 | else if (x < 0 && y > 0 && result > 0) throw new UnderflowException 88 | else result 89 | } 90 | 91 | def divide(x: Int, y: Int) = { 92 | if (y == 0) throw new MathCalculationException 93 | else x / y 94 | } 95 | 96 | } 97 | 98 | println(PocketCalculator.add(Int.MaxValue, 10)) 99 | println(PocketCalculator.divide(2, 0)) 100 | } 101 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/Generics.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object Generics extends App { 4 | 5 | class MyList[+A] { 6 | // use the type A 7 | def add[B >: A](element: B): MyList[B] = ??? 8 | /* 9 | A = Cat 10 | B = Animal 11 | */ 12 | } 13 | 14 | class MyMap[Key, Value] 15 | 16 | val listOfIntegers = new MyList[Int] 17 | val listOfStrings = new MyList[String] 18 | 19 | // generic methods 20 | object MyList { 21 | def empty[A]: MyList[A] = ??? 22 | } 23 | val emptyListOfIntegers = MyList.empty[Int] 24 | 25 | // variance problem 26 | // (don't stress about it) 27 | class Animal 28 | class Cat extends Animal 29 | class Dog extends Animal 30 | 31 | // 1. yes, List[Cat] extends List[Animal] = COVARIANCE 32 | class CovariantList[+A] 33 | val animal: Animal = new Cat 34 | val animalList: CovariantList[Animal] = new CovariantList[Cat] 35 | // animalList.add(new Dog) ??? HARD QUESTION => we return a list of Animals 36 | 37 | // 2. NO = INVARIANCE 38 | class InvariantList[A] 39 | val invariantAnimalList: InvariantList[Animal] = new InvariantList[Animal] 40 | 41 | // 3. Hell, no! CONTRAVARIANCE 42 | class Trainer[-A] 43 | val trainer: Trainer[Cat] = new Trainer[Animal] 44 | 45 | // bounded types 46 | class Cage[A <: Animal](animal: A) 47 | val cage = new Cage(new Dog) 48 | 49 | class Car 50 | // generic type needs proper bounded type 51 | // val newCage = new Cage(new Car) 52 | 53 | 54 | // expand MyList to be generic 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/Inheritance.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object Inheritance extends App { 4 | 5 | // single class inheritance 6 | sealed class Animal { 7 | val creatureType = "wild" 8 | def eat = println("nomnom") 9 | } 10 | 11 | class Cat extends Animal { 12 | def crunch = { 13 | eat 14 | println("crunch crunch") 15 | } 16 | } 17 | 18 | val cat = new Cat 19 | cat.crunch 20 | 21 | 22 | // constructors 23 | class Person(name: String, age: Int) { 24 | def this(name: String) = this(name, 0) 25 | } 26 | class Adult(name: String, age: Int, idCard: String) extends Person(name) 27 | 28 | // overriding 29 | class Dog(override val creatureType: String) extends Animal { 30 | // override val creatureType = "domestic" // can override in the body or directly in the constructor arguments 31 | override def eat = { 32 | super.eat 33 | println("crunch, crunch") 34 | } 35 | } 36 | val dog = new Dog("K9") 37 | dog.eat 38 | println(dog.creatureType) 39 | 40 | 41 | // type substitution (broad: polymorphism) 42 | val unknownAnimal: Animal = new Dog("K9") 43 | unknownAnimal.eat 44 | 45 | // overRIDING vs overLOADING 46 | 47 | // super 48 | 49 | // preventing overrides 50 | // 1 - use final on member 51 | // 2 - use final on the entire class 52 | // 3 - seal the class = extend classes in THIS FILE, prevent extension in other files 53 | } 54 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/MethodNotations.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | import scala.language.postfixOps 3 | 4 | object MethodNotations extends App { 5 | 6 | class Person(val name: String, favoriteMovie: String, val age: Int = 0) { 7 | def likes(movie: String): Boolean = movie == favoriteMovie 8 | def +(person: Person): String = s"${this.name} is hanging out with ${person.name}" 9 | def +(nickname: String): Person = new Person(s"$name ($nickname)", favoriteMovie) 10 | def unary_! : String = s"$name, what the heck?!" 11 | def unary_+ : Person = new Person(name, favoriteMovie, age + 1) 12 | def isAlive: Boolean = true 13 | def apply(): String = s"Hi, my name is $name and I like $favoriteMovie" 14 | def apply(n: Int): String = s"$name watched $favoriteMovie $n times" 15 | def learns(thing: String) = s"$name is learning $thing" 16 | def learnsScala = this learns "Scala" 17 | } 18 | 19 | val mary = new Person("Mary", "Inception") 20 | println(mary.likes("Inception")) 21 | println(mary likes "Inception") // equivalent 22 | // infix notation = operator notation (syntactic sugar) 23 | 24 | // "operators" in Scala 25 | val tom = new Person("Tom", "Fight Club") 26 | println(mary + tom) 27 | println(mary.+(tom)) 28 | 29 | println(1 + 2) 30 | println(1.+(2)) 31 | 32 | // ALL OPERATORS ARE METHODS. 33 | // Akka actors have ! ? 34 | 35 | // prefix notation 36 | val x = -1 // equivalent with 1.unary_- 37 | val y = 1.unary_- 38 | // unary_ prefix only works with - + ~ ! 39 | 40 | println(!mary) 41 | println(mary.unary_!) 42 | 43 | // postfix notation 44 | println(mary.isAlive) 45 | println(mary isAlive) // only available with the scala.language.postfixOps import - discouraged 46 | 47 | // apply 48 | println(mary.apply()) 49 | println(mary()) // equivalent 50 | 51 | /* 52 | 1. Overload the + operator 53 | mary + "the rockstar" => new person "Mary (the rockstar)" 54 | 55 | 2. Add an age to the Person class 56 | Add a unary + operator => new person with the age + 1 57 | +mary => mary with the age incrementer 58 | 59 | 3. Add a "learns" method in the Person class => "Mary learns Scala" 60 | Add a learnsScala method, calls learns method with "Scala". 61 | Use it in postfix notation. 62 | 63 | 4. Overload the apply method 64 | mary.apply(2) => "Mary watched Inception 2 times" 65 | */ 66 | 67 | println((mary + "the Rockstar").apply()) 68 | println((+mary).age) 69 | println(mary learnsScala) 70 | println(mary(10)) 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/OOBasics.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | import scala.annotation.tailrec 4 | 5 | object OOBasics extends App { 6 | 7 | // constructor 8 | class Person(name: String, val age: Int = 0) { 9 | // body 10 | val x = 2 11 | 12 | println(1 + 3) 13 | 14 | // method 15 | def greet(name: String): Unit = println(s"${this.name} says: Hi, $name") 16 | 17 | // overloading 18 | def greet(): Unit = println(s"Hi, I am $name") 19 | 20 | // multiple constructors 21 | def this(name: String) = this(name, 0) 22 | def this() = this("John Doe") 23 | } 24 | 25 | val person = new Person("John", 26) 26 | println(person.x) 27 | person.greet("Daniel") 28 | person.greet() 29 | 30 | val author = new Writer("Charles", "Dickens", 1812) 31 | val imposter = new Writer("Charles", "Dickens", 1812) 32 | val novel = new Novel("Great Expectations", 1861, author) 33 | 34 | println(novel.authorAge) 35 | println(novel.isWrittenBy(imposter)) 36 | 37 | val counter = new Counter 38 | counter.inc.print 39 | counter.inc.inc.inc.print 40 | counter.inc(10000).print 41 | } 42 | 43 | /* 44 | Novel and a Writer 45 | 46 | Writer: first name, surname, year 47 | - method fullname 48 | 49 | Novel: name, year of release, author 50 | - authorAge 51 | - isWrittenBy(author) 52 | - copy (new year of release) = new instance of Novel 53 | */ 54 | class Writer(firstName: String, surname: String, val year: Int) { 55 | def fullName: String = firstName + " " + surname 56 | } 57 | 58 | class Novel(name: String, year: Int, author: Writer) { 59 | def authorAge = year - author.year 60 | def isWrittenBy(author: Writer) = author == this.author 61 | def copy(newYear: Int): Novel = new Novel(name, newYear, author) 62 | } 63 | 64 | /* 65 | Counter class 66 | - receives an int value 67 | - method current count 68 | - method to increment/decrement => new Counter 69 | - overload inc/dec to receive an amount 70 | */ 71 | class Counter(val count: Int = 0) { 72 | def inc = { 73 | println("incrementing") 74 | new Counter(count + 1) // immutability 75 | } 76 | 77 | def dec = { 78 | println("decrementing") 79 | new Counter(count - 1) 80 | } 81 | 82 | def inc(n: Int): Counter = { 83 | if (n <= 0) this 84 | else inc.inc(n-1) 85 | } 86 | 87 | def dec(n: Int): Counter = 88 | if (n <= 0) this 89 | else dec.dec(n-1) 90 | 91 | def print = println(count) 92 | } 93 | 94 | // class parameters are NOT FIELDS -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/Objects.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | object Objects extends App { 4 | 5 | // SCALA DOES NOT HAVE CLASS-LEVEL FUNCTIONALITY ("static") 6 | object Person { // type + its only instance 7 | // "static"/"class" - level functionality 8 | val N_EYES = 2 9 | def canFly: Boolean = false 10 | 11 | // factory method 12 | def apply(mother: Person, father: Person): Person = new Person("Bobbie") 13 | } 14 | class Person(val name: String) { 15 | // instance-level functionality 16 | } 17 | // COMPANIONS 18 | 19 | println(Person.N_EYES) 20 | println(Person.canFly) 21 | 22 | // Scala object = SINGLETON INSTANCE 23 | val mary = new Person("Mary") 24 | val john = new Person("John") 25 | println(mary == john) 26 | 27 | val person1 = Person 28 | val person2 = Person 29 | println(person1 == person2) 30 | 31 | val bobbie = Person(mary, john) 32 | 33 | // Scala Applications = Scala object with 34 | // def main(args: Array[String]): Unit 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/PackagingAndImports.scala: -------------------------------------------------------------------------------- 1 | package lectures.part2oop 2 | 3 | import playground.{PrinceCharming, Cinderella => Princess} 4 | 5 | import java.util.Date 6 | import java.sql.{Date => SqlDate} 7 | 8 | object PackagingAndImports extends App { 9 | 10 | // package members are accessible by their simple name 11 | val writer = new Writer("Daniel", "RockTheJVM", 2018) 12 | 13 | // import the package 14 | val princess = new Princess // playground.Cinderella = fully qualified name 15 | 16 | // packages are in hierarchy 17 | // matching folder structure. 18 | 19 | // package object 20 | sayHello 21 | println(SPEED_OF_LIGHT) 22 | 23 | // imports 24 | val prince = new PrinceCharming 25 | 26 | // 1. use FQ names 27 | val date = new Date 28 | val sqlDate = new SqlDate(2018, 5, 4) 29 | // 2. use aliasing 30 | 31 | // default imports 32 | // java.lang - String, Object, Exception 33 | // scala - Int, Nothing, Function 34 | // scala.Predef - println, ??? 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part2oop/package.scala: -------------------------------------------------------------------------------- 1 | package lectures 2 | 3 | package object part2oop { 4 | def sayHello: Unit = println("Hello, Scala") 5 | val SPEED_OF_LIGHT = 299792458 6 | } 7 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/AnonymousFunctions.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | object AnonymousFunctions extends App { 4 | 5 | // anonymous function (LAMBDA) 6 | val doubler: Int => Int = (x: Int) => x * 2 7 | 8 | // multiple params in a lambda 9 | val adder: (Int, Int) => Int = (a: Int, b: Int) => a + b 10 | 11 | // no params 12 | val justDoSomething: () => Int = () => 3 13 | 14 | // careful 15 | println(justDoSomething) // function itself 16 | println(justDoSomething()) // call 17 | 18 | // curly braces with lambdas 19 | val stringToInt = { (str: String) => 20 | str.toInt 21 | } 22 | 23 | // MOAR syntactic sugar 24 | val niceIncrementer: Int => Int = _ + 1 // equivalent to x => x + 1 25 | val niceAdder: (Int, Int) => Int = _ + _ // equivalent to (a,b) => a + b 26 | 27 | /* 28 | 1. MyList: replace all FunctionX calls with lambdas 29 | 2. Rewrite the "special" adder as an anonymous function 30 | */ 31 | 32 | val superAdd = (x: Int) => (y: Int) => x + y 33 | println(superAdd(3)(4)) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/HOFsCurries.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | object HOFsCurries extends App { 4 | 5 | val superFunction: (Int, (String, (Int => Boolean)) => Int) => (Int => Int) = null 6 | // higher order function (HOF) 7 | 8 | // map, flatMap, filter in MyList 9 | 10 | // function that applies a function n times over a value x 11 | // nTimes(f, n, x) 12 | // nTimes(f, 3, x) = f(f(f(x))) = nTimes(f, 2, f(x)) = f(f(f(x))) 13 | // nTimes(f, n, x) = f(f(...f(x))) = nTimes(f, n-1, f(x)) 14 | def nTimes(f: Int => Int, n: Int, x: Int): Int = 15 | if (n <= 0) x 16 | else nTimes(f, n-1, f(x)) 17 | 18 | val plusOne = (x: Int) => x + 1 19 | println(nTimes(plusOne, 10, 1)) 20 | 21 | // ntb(f,n) = x => f(f(f...(x))) 22 | // increment10 = ntb(plusOne, 10) = x => plusOne(plusOne....(x)) 23 | // val y = increment10(1) 24 | def nTimesBetter(f: Int => Int, n: Int): (Int => Int) = 25 | if (n <= 0) (x: Int) => x 26 | else (x: Int) => nTimesBetter(f, n-1)(f(x)) 27 | 28 | val plus10 = nTimesBetter(plusOne, 100000) 29 | println(plus10(1)) 30 | 31 | // curried functions 32 | val superAdder: Int => (Int => Int) = (x: Int) => (y: Int) => x + y 33 | val add3 = superAdder(3) // y => 3 + y 34 | println(add3(10)) 35 | println(superAdder(3)(10)) 36 | 37 | // functions with multiple parameter lists 38 | def curriedFormatter(c: String)(x: Double): String = c.format(x) 39 | 40 | val standardFormat: (Double => String) = curriedFormatter("%4.2f") 41 | val preciseFormat: (Double => String) = curriedFormatter("%10.8f") 42 | 43 | println(standardFormat(Math.PI)) 44 | println(preciseFormat(Math.PI)) 45 | 46 | /* 47 | 1. Expand MyList 48 | - foreach method A => Unit 49 | [1,2,3].foreach(x => println(x)) 50 | 51 | - sort function ((A, A) => Int) => MyList 52 | [1,2,3].sort((x, y) => y - x) => [3,2,1] 53 | 54 | - zipWith (list, (A, A) => B) => MyList[B] 55 | [1,2,3].zipWith([4,5,6], x * y) => [1 * 4, 2 * 5, 3 * 6] = [4,10,18] 56 | 57 | - fold(start)(function) => a value 58 | [1,2,3].fold(0)(x + y) = 6 59 | 60 | 2. toCurry(f: (Int, Int) => Int) => (Int => Int => Int) 61 | fromCurry(f: (Int => Int => Int)) => (Int, Int) => Int 62 | 63 | 3. compose(f,g) => x => f(g(x)) 64 | andThen(f,g) => x => g(f(x)) 65 | */ 66 | 67 | def toCurry(f: (Int, Int) => Int): (Int => Int => Int) = 68 | x => y => f(x, y) 69 | 70 | def fromCurry(f: (Int => Int => Int)): (Int, Int) => Int = 71 | (x, y) => f(x)(y) 72 | 73 | // FunctionX 74 | def compose[A,B,T](f: A => B, g: T => A): T => B = 75 | x => f(g(x)) 76 | 77 | def andThen[A,B,C](f: A => B, g: B => C): A => C = 78 | x => g(f(x)) 79 | 80 | def superAdder2: (Int => Int => Int) = toCurry(_ + _) 81 | def add4 = superAdder2(4) 82 | println(add4(17)) 83 | 84 | val simpleAdder = fromCurry(superAdder) 85 | println(simpleAdder(4,17)) 86 | 87 | val add2 = (x: Int) => x + 2 88 | val times3 = (x: Int) => x * 3 89 | 90 | val composed = compose(add2, times3) 91 | val ordered = andThen(add2, times3) 92 | 93 | println(composed(4)) 94 | println(ordered(4)) 95 | } 96 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/HandlingFailure.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | import scala.util.{Random, Try, Failure, Success} 4 | 5 | object HandlingFailure extends App { 6 | 7 | // create success and failure 8 | val aSuccess = Success(3) 9 | val aFailure = Failure(new RuntimeException("SUPER FAILURE")) 10 | 11 | println(aSuccess) 12 | println(aFailure) 13 | 14 | def unsafeMethod(): String = throw new RuntimeException("NO STRING FOR YOU BUSTER") 15 | 16 | // Try objects via the apply method 17 | val potentialFailure = Try(unsafeMethod()) 18 | println(potentialFailure) 19 | 20 | // syntax sugar 21 | val anotherPotentialFailure = Try { 22 | // code that might throw 23 | } 24 | 25 | // utilities 26 | println(potentialFailure.isSuccess) 27 | 28 | // orElse 29 | def backupMethod(): String = "A valid result" 30 | val fallbackTry = Try(unsafeMethod()).orElse(Try(backupMethod())) 31 | println(fallbackTry) 32 | 33 | // IF you design the API 34 | def betterUnsafeMethod(): Try[String] = Failure(new RuntimeException) 35 | def betterBackupMethod(): Try[String] = Success("A valid result") 36 | val betterFallback = betterUnsafeMethod() orElse betterBackupMethod() 37 | 38 | // map, flatMap, filter 39 | println(aSuccess.map(_ * 2)) 40 | println(aSuccess.flatMap(x => Success(x * 10))) 41 | println(aSuccess.filter(_ > 10)) 42 | // => for-comprehensions 43 | 44 | /* 45 | Exercise 46 | */ 47 | val host = "localhost" 48 | val port = "8080" 49 | def renderHTML(page: String) = println(page) 50 | 51 | class Connection { 52 | def get(url: String): String = { 53 | val random = new Random(System.nanoTime()) 54 | if (random.nextBoolean()) "..." 55 | else throw new RuntimeException("Connection interrupted") 56 | } 57 | 58 | def getSafe(url: String): Try[String] = Try(get(url)) 59 | } 60 | 61 | object HttpService { 62 | val random = new Random(System.nanoTime()) 63 | 64 | def getConnection(host: String, port: String): Connection = 65 | if (random.nextBoolean()) new Connection 66 | else throw new RuntimeException("Someone else took the port") 67 | 68 | def getSafeConnection(host: String, port: String): Try[Connection] = Try(getConnection(host, port)) 69 | } 70 | 71 | // if you get the html page from the connection, print it to the console i.e. call renderHTML 72 | val possibleConnection = HttpService.getSafeConnection(host, port) 73 | val possibleHTML = possibleConnection.flatMap(connection => connection.getSafe("/home")) 74 | possibleHTML.foreach(renderHTML) 75 | 76 | // shorthand version 77 | HttpService.getSafeConnection(host, port) 78 | .flatMap(connection => connection.getSafe("/home")) 79 | .foreach(renderHTML) 80 | 81 | // for-comprehension version 82 | 83 | /* 84 | try { 85 | connection = HttpService.getConnection(host, port) 86 | try { 87 | page = connection.get("/home") 88 | renderHTML(page) 89 | } catch (some other exception) { 90 | 91 | } 92 | } catch (exception) { 93 | 94 | } 95 | */ 96 | } 97 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/MapFlatmapFilterFor.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | object MapFlatmapFilterFor extends App { 4 | 5 | val list = List(1,2,3) 6 | println(list.head) 7 | println(list.tail) 8 | 9 | // map 10 | println(list.map(_ + 1)) 11 | println(list.map(_ + " is a number")) 12 | 13 | // filter 14 | println(list.filter(_ % 2 == 0)) 15 | 16 | // flatMap 17 | val toPair = (x: Int) => List(x, x+1) 18 | println(list.flatMap(toPair)) 19 | 20 | // print all combinations between two lists 21 | val numbers = List(1,2,3,4) 22 | val chars = List('a','b','c','d') 23 | val colors = List("black", "white") 24 | 25 | // List("a1", "a2"... "d4") 26 | 27 | // "iterating" 28 | val combinations = numbers.filter(_ % 2 == 0).flatMap(n => chars.flatMap(c => colors.map(color => "" + c + n + "-" + color))) 29 | println(combinations) 30 | 31 | 32 | // foreach 33 | list.foreach(println) 34 | 35 | // for-comprehensions 36 | val forCombinations = for { 37 | n <- numbers if n % 2 == 0 38 | c <- chars 39 | color <- colors 40 | } yield "" + c + n + "-" + color 41 | println(forCombinations) 42 | 43 | for { 44 | n <- numbers 45 | } println(n) 46 | 47 | // syntax overload 48 | list.map { x => 49 | x * 2 50 | } 51 | 52 | /* 53 | 1. MyList supports for comprehensions? 54 | map(f: A => B) => MyList[B] 55 | filter(p: A => Boolean) => MyList[A] 56 | flatMap(f: A => MyList[B]) => MyList[B] 57 | 2. A small collection of at most ONE element - Maybe[+T] 58 | - map, flatMap, filter 59 | */ 60 | } 61 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/Options.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | import java.util.Random 4 | 5 | object Options extends App { 6 | 7 | val myFirstOption: Option[Int] = Some(4) 8 | val noOption: Option[Int] = None 9 | 10 | println(myFirstOption) 11 | 12 | // WORK with unsafe APIs 13 | def unsafeMethod(): String = null 14 | // val result = Some(null) // WRONG 15 | val result = Option(unsafeMethod()) // Some or None 16 | println(result) 17 | 18 | // chained methods 19 | def backupMethod(): String = "A valid result" 20 | val chainedResult = Option(unsafeMethod()).orElse(Option(backupMethod())) 21 | 22 | // DESIGN unsafe APIs 23 | def betterUnsafeMethod(): Option[String] = None 24 | def betterBackupMethod(): Option[String] = Some("A valid result") 25 | val betterChainedResult = betterUnsafeMethod() orElse betterBackupMethod() 26 | 27 | // functions on Options 28 | println(myFirstOption.isEmpty) 29 | println(myFirstOption.get) // USAFE - DO NOT USE THIS 30 | 31 | // map, flatMap, filter 32 | println(myFirstOption.map(_ * 2)) 33 | println(myFirstOption.filter(x => x > 10)) 34 | println(myFirstOption.flatMap(x => Option(x * 10))) 35 | 36 | // for-comprehensions 37 | 38 | /* 39 | Exercise. 40 | */ 41 | val config: Map[String, String] = Map( 42 | // fetched from elsewhere 43 | "host" -> "176.45.36.1", 44 | "port" -> "80" 45 | ) 46 | 47 | class Connection { 48 | def connect = "Connected" // connect to some server 49 | } 50 | object Connection { 51 | val random = new Random(System.nanoTime()) 52 | 53 | def apply(host: String, port: String): Option[Connection] = 54 | if (random.nextBoolean()) Some(new Connection) 55 | else None 56 | } 57 | 58 | // try to establish a connection, if so - print the connect method 59 | val host = config.get("host") 60 | val port = config.get("port") 61 | /* 62 | if (h != null) 63 | if (p != null) 64 | return Connection.apply(h, p) 65 | 66 | return null 67 | */ 68 | val connection = host.flatMap(h => port.flatMap(p => Connection.apply(h, p))) 69 | /* 70 | if (c != null) 71 | return c.connect 72 | return null 73 | */ 74 | val connectionStatus = connection.map(c => c.connect) 75 | // if (connectionStatus == null) println(None) else print (Some(connectionstatus.get)) 76 | println(connectionStatus) 77 | /* 78 | if (status != null) 79 | println(status) 80 | */ 81 | connectionStatus.foreach(println) 82 | 83 | // chained calls 84 | config.get("host") 85 | .flatMap(host => config.get("port") 86 | .flatMap(port => Connection(host, port)) 87 | .map(connection => connection.connect)) 88 | .foreach(println) 89 | 90 | // for-comprehensions 91 | val forConnectionStatus = for { 92 | host <- config.get("host") 93 | port <- config.get("port") 94 | connection <- Connection(host, port) 95 | } yield connection.connect 96 | 97 | forConnectionStatus.foreach(println) 98 | } 99 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/Sequences.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | import scala.util.Random 4 | 5 | object Sequences extends App { 6 | 7 | // Seq 8 | val aSequence = Seq(1,3,2,4) 9 | println(aSequence) 10 | println(aSequence.reverse) 11 | println(aSequence(2)) 12 | println(aSequence ++ Seq(7,5,6)) 13 | println(aSequence.sorted) 14 | 15 | // Ranges 16 | val aRange: Seq[Int] = 1 until 10 17 | aRange.foreach(println) 18 | 19 | (1 to 10).foreach(x => println("Hello")) 20 | 21 | // lists 22 | val aList = List(1,2,3) 23 | val prepended = 42 +: aList :+ 89 24 | println(prepended) 25 | 26 | val apples5 = List.fill(5)("apple") 27 | println(apples5) 28 | println(aList.mkString("-|-")) 29 | 30 | // arrays 31 | val numbers = Array(1,2,3,4) 32 | val threeElements = Array.ofDim[String](3) 33 | threeElements.foreach(println) 34 | 35 | // mutation 36 | numbers(2) = 0 // syntax sugar for numbers.update(2, 0) 37 | println(numbers.mkString(" ")) 38 | 39 | // arrays and seq 40 | val numbersSeq: Seq[Int] = numbers // implicit conversion 41 | println(numbersSeq) 42 | 43 | // vectors 44 | val vector: Vector[Int] = Vector(1,2,3) 45 | println(vector) 46 | 47 | // vectors vs lists 48 | 49 | val maxRuns = 1000 50 | val maxCapacity = 1000000 51 | 52 | def getWriteTime(collection: Seq[Int]): Double = { 53 | val r = new Random 54 | val times = for { 55 | it <- 1 to maxRuns 56 | } yield { 57 | val currentTime = System.nanoTime() 58 | collection.updated(r.nextInt(maxCapacity), r.nextInt()) 59 | System.nanoTime() - currentTime 60 | } 61 | 62 | times.sum * 1.0 / maxRuns 63 | } 64 | 65 | val numbersList = (1 to maxCapacity).toList 66 | val numbersVector = (1 to maxCapacity).toVector 67 | 68 | // keeps reference to tail 69 | // updating an element in the middle takes long 70 | println(getWriteTime(numbersList)) 71 | // depth of the tree is small 72 | // needs to replace an entire 32-element chunk 73 | println(getWriteTime(numbersVector)) 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/TuplesAndMaps.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | import scala.annotation.tailrec 4 | 5 | /** 6 | * Created by Daniel. 7 | */ 8 | object TuplesAndMaps extends App { 9 | 10 | // tuples = finite ordered "lists" 11 | val aTuple = (2, "hello, Scala") // Tuple2[Int, String] = (Int, String) 12 | 13 | println(aTuple._1) // 2 14 | println(aTuple.copy(_2 = "goodbye Java")) 15 | println(aTuple.swap) // ("hello, Scala", 2) 16 | 17 | // Maps - keys -> values 18 | val aMap: Map[String, Int] = Map() 19 | 20 | val phonebook = Map(("Jim", 555), "Daniel" -> 789, ("JIM", 9000)).withDefaultValue(-1) 21 | // a -> b is sugar for (a, b) 22 | println(phonebook) 23 | 24 | // map ops 25 | println(phonebook.contains("Jim")) 26 | println(phonebook("Mary")) 27 | 28 | // add a pairing 29 | val newPairing = "Mary" -> 678 30 | val newPhonebook = phonebook + newPairing 31 | println(newPhonebook) 32 | 33 | // functionals on maps 34 | // map, flatMap, filter 35 | // println(phonebook.map(pair => pair._1.toLowerCase -> pair._2)) 36 | 37 | // filterKeys 38 | // println(phonebook.view.filterKeys(x => x.startsWith("J")).toMap) 39 | // mapValues 40 | println(phonebook.view.mapValues(number => "0245-" + number).toMap) 41 | 42 | // conversions to other collections 43 | println(phonebook.toList) 44 | // println(List(("Daniel", 555)).toMap) 45 | val names = List("Bob", "James", "Angela", "Mary", "Daniel", "Jim") 46 | println(names.groupBy(name => name.charAt(0))) 47 | println(names.groupBy(name => name.charAt(0) == 'J')) 48 | 49 | /* 50 | 1. What would happen if I had two original entries "Jim" -> 555 and "JIM" -> 900 51 | 52 | !!! careful with mapping keys. 53 | 54 | 2. Overly simplified social network based on maps 55 | Person = String 56 | - add a person to the network 57 | - remove 58 | - friend (mutual) 59 | - unfriend 60 | 61 | - number of friends of a person 62 | - person with most friends 63 | - how many people have NO friends 64 | - if there is a social connection between two people (direct or not) 65 | */ 66 | def add(network: Map[String, Set[String]], person: String): Map[String, Set[String]] = 67 | network + (person -> Set()) 68 | 69 | def friend(network: Map[String, Set[String]], a: String, b: String): Map[String, Set[String]] = { 70 | val friendsA = network(a) 71 | val friendsB = network(b) 72 | 73 | network + (a -> (friendsA + b)) + (b -> (friendsB + a)) 74 | } 75 | 76 | def unfriend(network: Map[String, Set[String]], a: String, b: String): Map[String, Set[String]] = { 77 | val friendsA = network(a) 78 | val friendsB = network(b) 79 | 80 | network + (a -> (friendsA - b)) + (b -> (friendsB - a)) 81 | } 82 | 83 | def remove(network: Map[String, Set[String]], person: String): Map[String, Set[String]] = { 84 | def removeAux(friends: Set[String], networkAcc: Map[String, Set[String]]): Map[String, Set[String]] = 85 | if (friends.isEmpty) networkAcc 86 | else removeAux(friends.tail, unfriend(networkAcc, person, friends.head)) 87 | 88 | val unfriended = removeAux(network(person), network) 89 | unfriended - person 90 | } 91 | 92 | val empty: Map[String, Set[String]] = Map() 93 | val network = add(add(empty, "Bob"), "Mary") 94 | println(network) 95 | println(friend(network, "Bob", "Mary")) 96 | println(unfriend(friend(network, "Bob", "Mary"), "Bob", "Mary")) 97 | println(remove(friend(network, "Bob", "Mary"), "Bob")) 98 | 99 | // Jim,Bob,Mary 100 | val people = add(add(add(empty, "Bob"), "Mary"), "Jim") 101 | val jimBob = friend(people, "Bob", "Jim") 102 | val testNet = friend(jimBob, "Bob", "Mary") 103 | 104 | println(testNet) 105 | 106 | def nFriends(network: Map[String, Set[String]], person: String): Int = 107 | if (!network.contains(person)) 0 108 | else network(person).size 109 | 110 | println(nFriends(testNet, "Bob")) 111 | 112 | def mostFriends(network: Map[String, Set[String]]): String = 113 | network.maxBy(pair => pair._2.size)._1 114 | 115 | println(mostFriends(testNet)) 116 | 117 | def nPeopleWithNoFriends(network: Map[String, Set[String]]): Int = 118 | network.count(_._2.isEmpty) 119 | 120 | println(nPeopleWithNoFriends(testNet)) 121 | 122 | def socialConnection(network: Map[String, Set[String]], a: String, b: String): Boolean = { 123 | @tailrec 124 | def bfs(target: String, consideredPeople: Set[String], discoveredPeople: Set[String]): Boolean = { 125 | if (discoveredPeople.isEmpty) false 126 | else { 127 | val person = discoveredPeople.head 128 | if (person == target) true 129 | else if (consideredPeople.contains(person)) bfs(target, consideredPeople, discoveredPeople.tail) 130 | else bfs(target, consideredPeople + person, discoveredPeople.tail ++ network(person)) 131 | } 132 | } 133 | 134 | bfs(b, Set(), network(a) + a) 135 | } 136 | 137 | println(socialConnection(testNet, "Mary", "Jim")) 138 | println(socialConnection(network, "Mary", "Bob")) 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part3fp/WhatsAFunction.scala: -------------------------------------------------------------------------------- 1 | package lectures.part3fp 2 | 3 | object WhatsAFunction extends App { 4 | 5 | // DREAM: use functions as first class elements 6 | // problem: oop 7 | 8 | val doubler = new MyFunction[Int, Int] { 9 | override def apply(element: Int): Int = element * 2 10 | } 11 | 12 | println(doubler(2)) 13 | 14 | // function types = Function1[A, B] 15 | val stringToIntConverter = new Function1[String, Int] { 16 | override def apply(string: String): Int = string.toInt 17 | } 18 | 19 | println(stringToIntConverter("3") + 4) 20 | 21 | val adder: ((Int, Int) => Int) = new Function2[Int, Int, Int] { 22 | override def apply(a: Int, b: Int): Int = a + b 23 | } 24 | 25 | // Function types Function2[A, B, R] === (A,B) => R 26 | 27 | // ALL SCALA FUNCTIONS ARE OBJECTS 28 | 29 | /* 30 | 1. a function which takes 2 strings and concatenates them 31 | 2. transform the MyPredicate and MyTransformer into function types 32 | 3. define a function which takes an int and returns another function which takes an int and returns an int 33 | - what's the type of this function 34 | - how to do it 35 | */ 36 | 37 | def concatenator: (String, String) => String = new Function2[String, String, String] { 38 | override def apply(a: String, b: String): String = a + b 39 | } 40 | println(concatenator("Hello ", "Scala")) 41 | 42 | // Function1[Int, Function1[Int, Int]] 43 | val superAdder: Function1[Int, Function1[Int, Int]] = new Function1[Int, Function1[Int, Int]] { 44 | override def apply(x: Int): Function1[Int, Int] = new Function1[Int, Int] { 45 | override def apply(y: Int): Int = x + y 46 | } 47 | } 48 | 49 | val adder3 = superAdder(3) 50 | println(adder3(4)) 51 | println(superAdder(3)(4)) // curried function 52 | 53 | } 54 | 55 | trait MyFunction[A, B] { 56 | def apply(element: A): B 57 | } -------------------------------------------------------------------------------- /src/main/scala/lectures/part4power/AllThePatterns.scala: -------------------------------------------------------------------------------- 1 | package lectures.part4power 2 | 3 | import exercises._ 4 | 5 | object AllThePatterns extends App { 6 | 7 | // 1 - constants 8 | val x: Any = "Scala" 9 | val constants = x match { 10 | case 1 => "a number" 11 | case "Scala" => "THE Scala" 12 | case true => "The Truth" 13 | case AllThePatterns => "A singleton object" 14 | } 15 | 16 | // 2 - match anything 17 | // 2.1 wildcard 18 | val matchAnything = x match { 19 | case _ => 20 | } 21 | 22 | // 2.2 variable 23 | val matchAVariable = x match { 24 | case something => s"I've found $something" 25 | } 26 | 27 | // 3 - tuples 28 | val aTuple = (1,2) 29 | val matchATuple = aTuple match { 30 | case (1, 1) => 31 | case (something, 2) => s"I've found $something" 32 | } 33 | 34 | val nestedTuple = (1, (2, 3)) 35 | val matchANestedTuple = nestedTuple match { 36 | case (_, (2, v)) => 37 | } 38 | // PMs can be NESTED! 39 | 40 | // 4 - case classes - constructor pattern 41 | // PMs can be nested with CCs as well 42 | val aList: MyList[Int] = Cons(1, Cons(2, Empty)) 43 | val matchAList = aList match { 44 | case Empty => 45 | case Cons(head, Cons(subhead, subtail)) => 46 | } 47 | 48 | // 5 - list patterns 49 | val aStandardList = List(1,2,3,42) 50 | val standardListMatching = aStandardList match { 51 | case List(1, _, _, _) => // extractor - advanced 52 | case List(1, _*) => // list of arbitrary length - advanced 53 | case 1 :: List(_) => // infix pattern 54 | case List(1,2,_) :+ 42 => "lala"// infix pattern 55 | } 56 | 57 | // 6 - type specifiers 58 | val unknown: Any = 2 59 | val unknownMatch = unknown match { 60 | case list: List[Int] => // explicit type specifier 61 | case _ => 62 | } 63 | 64 | // 7 - name binding 65 | val nameBindingMatch = aList match { 66 | case nonEmptyList @ Cons(_, _) => // name binding => use the name later(here) 67 | case Cons(1, rest @ Cons(2, _)) => // name binding inside nested patterns 68 | } 69 | 70 | // 8 - multi-patterns 71 | val multipattern = aList match { 72 | case Empty | Cons(0, _) => // compound pattern (multi-pattern) 73 | } 74 | 75 | // 9 - if guards 76 | val secondElementSpecial = aList match { 77 | case Cons(_, Cons(specialElement, _)) if specialElement % 2 == 0 => 78 | } 79 | 80 | val anything: Any = ??? 81 | anything match { 82 | case _: RuntimeException | _:NullPointerException => "" 83 | } 84 | 85 | // ALL. 86 | 87 | /* 88 | Question. 89 | */ 90 | 91 | val numbers = List(1,2,3) 92 | val numbersMatch = numbers match { 93 | case listOfStrings: List[String] => "a list of strings" 94 | case listOfNumbers: List[Int] => "a list of numbers" 95 | case _ => "" 96 | } 97 | 98 | println(numbersMatch) 99 | // JVM trick question 100 | } 101 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part4power/BracelessSyntax.scala: -------------------------------------------------------------------------------- 1 | package lectures.part4power 2 | 3 | object BracelessSyntax { 4 | 5 | // if - expressions 6 | val anIfExpression = if (2 > 3) "bigger" else "smaller" 7 | 8 | // java-style 9 | val anIfExpression_v2 = 10 | if (2 > 3) { 11 | "bigger" 12 | } else { 13 | "smaller" 14 | } 15 | 16 | // compact 17 | val anIfExpression_v3 = 18 | if (2 > 3) "bigger" 19 | else "smaller" 20 | 21 | // scala 3 22 | val anIfExpression_v4 = 23 | if 2 > 3 then 24 | "bigger" // higher indentation than the if part 25 | else 26 | "smaller" 27 | 28 | val anIfExpression_v5 = 29 | if 2 > 3 then 30 | val result = "bigger" 31 | result 32 | else 33 | val result = "smaller" 34 | result 35 | 36 | // scala 3 one-liner 37 | val anIfExpression_v6 = if 2 > 3 then "bigger" else "smaller" 38 | 39 | // for comprehensions 40 | val aForComprehension = for { 41 | n <- List(1,2,3) 42 | s <- List("black", "white") 43 | } yield s"$n$s" 44 | 45 | // scala 3 46 | val aForComprehension_v2 = 47 | for 48 | n <- List(1,2,3) 49 | s <- List("black", "white") 50 | yield s"$n$s" 51 | 52 | // pattern matching 53 | val meaningOfLife = 42 54 | val aPatternMatch = meaningOfLife match { 55 | case 1 => "the one" 56 | case 2 => "double or nothing" 57 | case _ => "something else" 58 | } 59 | 60 | // scala 3 61 | val aPatternMatch_v2 = 62 | meaningOfLife match 63 | case 1 => "the one" 64 | case 2 => "double or nothing" 65 | case _ => "something else" 66 | 67 | // methods without braces 68 | def computeMeaningOfLife(arg: Int): Int = // significant indentation starts here - think of it like a phantom code block 69 | val partialResult = 40 70 | 71 | 72 | 73 | 74 | 75 | partialResult + 2 // still part of the method implementation! 76 | 77 | // class definition with significant indentation (same for traits, objects, enums etc) 78 | class Animal: // compiler expects the body of Animal 79 | def eat(): Unit = 80 | println("I'm eating") 81 | end eat 82 | 83 | def grow(): Unit = 84 | println("I'm growing") 85 | 86 | // 3000 more lines of code 87 | end Animal // if, match, for, methods, classes, traits, enums, objects 88 | 89 | // anonymous classes 90 | val aSpecialAnimal = new Animal: 91 | override def eat(): Unit = println("I'm special") 92 | 93 | // indentation = strictly larger indentation 94 | // 3 spaces + 2 tabs > 2 spaces + 2 tabs 95 | // 3 spaces + 2 tabs > 3 spaces + 1 tab 96 | // 3 tabs + 2 spaces ??? 2 tabs + 3 spaces 97 | 98 | 99 | def main(args: Array[String]): Unit = { 100 | println(computeMeaningOfLife(78)) 101 | } 102 | } -------------------------------------------------------------------------------- /src/main/scala/lectures/part4power/PatternMatching.scala: -------------------------------------------------------------------------------- 1 | package lectures.part4power 2 | 3 | import scala.util.Random 4 | 5 | object PatternMatching extends App { 6 | 7 | // switch on steroids 8 | val random = new Random 9 | val x = random.nextInt(10) 10 | 11 | val description = x match { 12 | case 1 => "the ONE" 13 | case 2 => "double or nothing" 14 | case 3 => "third time is the charm" 15 | case _ => "something else" // _ = WILDCARD 16 | } 17 | 18 | println(x) 19 | println(description) 20 | 21 | // 1. Decompose values 22 | case class Person(name: String, age: Int) 23 | val bob = Person("Bob", 20) 24 | 25 | val greeting = bob match { 26 | case Person(n, a) if a < 21 => s"Hi, my name is $n and I can't drink in the US" 27 | case Person(n, a) => s"Hi, my name is $n and I am $a years old" 28 | case _ => "I don't know who I am" 29 | } 30 | println(greeting) 31 | 32 | /* 33 | 1. cases are matched in order 34 | 2. what if no cases match? MatchError 35 | 3. type of the PM expressoion? unified type of all the types in all the cases 36 | 4. PM works really well with case classes* 37 | */ 38 | 39 | // PM on sealed hierarchies 40 | sealed class Animal 41 | case class Dog(breed: String) extends Animal 42 | case class Parrot(greeting: String) extends Animal 43 | 44 | val animal: Animal = Dog("Terra Nova") 45 | animal match { 46 | case Dog(someBreed) => println(s"Matched a dog of the $someBreed breed") 47 | } 48 | 49 | // match everything 50 | val isEven = x match { 51 | case n if n % 2 == 0 => true 52 | case _ => false 53 | } 54 | // WHY?! 55 | val isEvenCond = if (x % 2 == 0) true else false // ?! 56 | val isEvenNormal = x % 2 == 0 57 | 58 | /* 59 | Exercise 60 | simple function uses PM 61 | takes an Expr => human readable form 62 | 63 | Sum(Number(2), Number(3)) => 2 + 3 64 | Sum(Number(2), Number(3), Number(4)) => 2 + 3 + 4 65 | Prod(Sum(Number(2), Number(1)), Number(3)) = (2 + 1) * 3 66 | Sum(Prod(Number(2), Number(1)), Number(3)) = 2 * 1 + 3 67 | */ 68 | trait Expr 69 | case class Number(n: Int) extends Expr 70 | case class Sum(e1: Expr, e2: Expr) extends Expr 71 | case class Prod(e1: Expr, e2: Expr) extends Expr 72 | 73 | def show(e: Expr): String = e match { 74 | case Number(n) => s"$n" 75 | case Sum(e1, e2) => show(e1) + " + " + show(e2) 76 | case Prod(e1, e2) => { 77 | def maybeShowParentheses(exp: Expr) = exp match { 78 | case Prod(_, _) => show(exp) 79 | case Number(_) => show(exp) 80 | case _ => "(" + show(exp) + ")" 81 | } 82 | 83 | maybeShowParentheses(e1) + " * " + maybeShowParentheses(e2) 84 | } 85 | } 86 | 87 | println(show(Sum(Number(2), Number(3)))) 88 | println(show(Sum(Sum(Number(2), Number(3)), Number(4)))) 89 | println(show(Prod(Sum(Number(2), Number(1)), Number(3)))) 90 | println(show(Prod(Sum(Number(2), Number(1)), Sum(Number(3), Number(4))))) 91 | println(show(Sum(Prod(Number(2), Number(1)), Number(3)))) 92 | } 93 | -------------------------------------------------------------------------------- /src/main/scala/lectures/part4power/PatternsEverywhere.scala: -------------------------------------------------------------------------------- 1 | package lectures.part4power 2 | 3 | object PatternsEverywhere extends App { 4 | 5 | // big idea #1 6 | try { 7 | // code 8 | } catch { 9 | case e: RuntimeException => "runtime" 10 | case npe: NullPointerException => "npe" 11 | case _ => "something else" 12 | } 13 | 14 | // catches are actually MATCHES 15 | /* 16 | try { 17 | // code 18 | } catch (e) { 19 | e match { 20 | case e: RuntimeException => "runtime" 21 | case npe: NullPointerException => "npe" 22 | case _ => "something else" 23 | } 24 | } 25 | */ 26 | 27 | // big idea #2 28 | val list = List(1,2,3,4) 29 | val evenOnes = for { 30 | x <- list if x % 2 == 0 // ?! 31 | } yield 10 * x 32 | 33 | // generators are also based on PATTERN MATCHING 34 | val tuples = List((1,2), (3,4)) 35 | val filterTuples = for { 36 | (first, second) <- tuples 37 | } yield first * second 38 | // case classes, :: operators, ... 39 | 40 | // big idea #3 41 | val tuple = (1,2,3) 42 | val (a, b, c) = tuple 43 | println(b) 44 | // multiple value definitions based on PATTERN MATCHING 45 | // ALL THE POWER 46 | 47 | val head :: tail = list 48 | println(head) 49 | println(tail) 50 | 51 | // big idea #4 - NEW 52 | // partial function based on PATTERN MATCHING 53 | val mappedList = list.map { 54 | case v if v % 2 == 0 => v + " is even" 55 | case 1 => "the one" 56 | case _ => "something else" 57 | } // partial function literal 58 | 59 | val mappedList2 = list.map { x => x match { 60 | case v if v % 2 == 0 => v + " is even" 61 | case 1 => "the one" 62 | case _ => "something else" 63 | } 64 | } 65 | println(mappedList) 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/scala/playground/Cinderella.scala: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | class Cinderella { 4 | // intentionally empty 5 | } 6 | -------------------------------------------------------------------------------- /src/main/scala/playground/JavaPlayground.java: -------------------------------------------------------------------------------- 1 | package playground; 2 | 3 | // class which makes the analogies between Scala objects and Java statics 4 | public class JavaPlayground { 5 | public static void main(String args[]) { 6 | System.out.println(Person.N_EYES); 7 | } 8 | } 9 | 10 | class Person { 11 | public static final int N_EYES = 2; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/scala/playground/PrinceCharming.scala: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | class PrinceCharming { 4 | // intentionally empty 5 | } 6 | -------------------------------------------------------------------------------- /src/main/scala/playground/ScalaPlayground.scala: -------------------------------------------------------------------------------- 1 | package playground 2 | 3 | object ScalaPlayground { 4 | val coupons = List("9A75786CBB0D73737F881312E471F6", "A1F45471CA8F869DDA1B6CB4096EA5", "0EC86BD02CC5A09E8F07F711293E02", "7B0E3C777FCE0A71D02CA0D2896C49", "425CD73008930291291FA8DF3E97C9", "04E3B23404E10B767A634012CE83A1", "28DEBD90E21CB4611662DB36C80B17", "89F05B8267B216E1BC71F85DDDC867", "2752D462DC5628EC1FAB1A016B0001", "1AB9B45755CE6608E52829A9C7228B", "2B6F7BF749B24439CF9E7DDDCAF9F1", "B4F0FB6463D6C25D0650D82985060C", "6F37DAA40986E219C3AF83843730E4", "41C3FF2EF9C580136A75BE4471EF3B", "206C02C00610F0C671AF18A253917B", "B5B614BAEAE02695A9117B95E2629F", "63B728195995FDBB16BC6A785C7E55", "B47E5BD6935AED36B3F521E005F603", "021F835C14D5B9D1B93C880FA197C9", "F12EEFEAEABC92D917999211D73A8F", "8FAF23EFBA12920D536F5B28DB0C1B", "F0A907F591FA9C53D524E9D46D3610", "3E8E68C437FD0E6E226B0BAA44E0A1", "FF42C4D4E1F6BDBD885039421F3415", "25B02B17D5EFBB9B444004BD49A570", "0F371F1520365979486175C6FAFA17", "20098D9735402F4D40273BBA571019", "52176FBBE55EDA55E918D6052932DA", "885531A18FF3A35E65F1470C688482", "CB08556DF6EDDCCB0015F8569F865C", "1D5C9264A46BB45330782A33375FED", "7AB6124AA61B806EC2A013E5DF6E9A", "8E264667A6406497E2D5C1E8E88C2E", "D087191A6687AEABDD42BCB43F9155", "730DFF230B472D86C44C7F478EA298", "E4AEDEA38F6C7F139463AA663CD60A", "7668E3E2C84A7E64BE87CD76B5836B", "666E27639254F97F3A94805B4BC65E", "C53324538ED207BE3E3BDD794791A0", "FEA91FB28CEA49673E6494DD54DD01", "D9FDBA83E099D1E4D07499AE485FD4", "CC9E2AD31622D8B5D8226CBE82CE13", "E917E1F507187395B505D1896D718E", "635584681604B1FC9BFD552C2039A2", "2E45DC81538ECDF8039CC614F2DCA9", "C68576809D7E300825BBFA447ADDA7", "693074FF20112C1F25CD77D675038E", "112AA2A9D8E57AD67BD57C1AC7F767", "56B5A1FD9F6809CB0E9AC2107629A2", "AE1546D0C5F88C31A9936A41993D0D", "DC292E4DC67688CEB6E129032FF5DC", "166CB9783388524503DF4A285A24A2", "42163C039186C7A39A35819C9BF736", "6F5478EB0326C68DD804C0753961F1", "1331B124B034982844DD216FB68C79", "2998157E932D6FE6E4E3404479F06D", "6E3C47B39BB7EE1EF4B836586095F6", "DAF57424B58A68ECF4E630A8FF0E15", "7D2DFA01FD69C6EDF99332CD8DAB8D", "16CADB3C470C304E92B3FB33BD527E", "51D4D4D288B2419854C1FEEF33EB8F", "03C98AA1FD22FEE324D5B7D52BA3DF", "FE82CC4A273B4B1FE0AD9BCFC0B385", "44DE72B324C59732E5B26DCFC5EBF6", "B655AA1972CC40E3AB130612CE25B7", "0165718F916FCC4D83AD5DF4BB7240", "E41A211A95688BCC974FC8E2DB473B", "BF03F8C6499FD6B33E4BEA459F863F", "1CCA8557BA0A7CF64FD173A18438D0", "5C1A3226412D22B70F41ADC82E8947", "5D67F2915890382BF2B20E9647CB85", "1FE7D12785A48B4CDBE1F074A29446", "5F89DAF7DF129E2661A3469EDD76C1", "462DB0D4A56D8C4E5B70BB7DA1DEB5", "A64A6DC443C221878DCBE3C6218A01", "FE2917320FA11B57F1E89309C11BB3", "B9741A8F7C45381F49797E97119A93", "4E54C8C1E6EEB484532A5DFA0E3BE0", "E0A333C0D6216F45C16586D4C129C8", "60F3B14E570D6E5B717D98D602DB8A", "0FE8953A6CDD9597E0BC6A5DFB81BA", "390B3B875CE28755042AE44A3E2015", "98D64F4AC06F252BAE8CEB31660948", "B784E8EC9781D718EF5A5D05FFB0D5", "04202422416F6715C18D81AE67057D", "B99B0C7A994296E5F782402C4A7C79", "71A5A94D478CCF369185124A6FA4E2", "230E3861FE64D814BE3C0283219521", "07AFFC059CAB940C73B004E51A2A64", "202A8904166368407EACFEEEDF0B7C", "42E4E90E59D669BEE62B5D757B7E0F", "0959FC88E5E7542AB08314672FC1BD", "6F424168757D26A2CA82DB3303B3DB", "03C10AF9781393980AF7536D7E6BF3", "F433D28F1C23CCA4EC1FDD60AC881E", "D8BFEF82A9D090DE5A2507805DF1B3", "A70C8514F8B9F6123EB6145D9368AB", "BC6AB46BC45FAC131AD5A14C4725D7", "22C8D0149801CFD566D470EEFE7C0E", "50655BA59634FD8D3B0F4153C56409") 5 | 6 | def printCoupons = { 7 | val courses = Map( 8 | "Scala 3 Essentials" -> "scala", 9 | "Scala 3 Advanced" -> "advanced-scala", 10 | "Cats" -> "cats", 11 | "Cats Effect" -> "cats-effect", 12 | "Spark Essentials" -> "spark-essentials", 13 | 14 | 15 | ) 16 | } 17 | 18 | class Inva[T] 19 | class Contra[-T] { 20 | def impound[S <: T](inva: Inva[S]): Contra[S] = ??? 21 | } 22 | 23 | def main(args: Array[String]): Unit = { 24 | println("Ready to learn Scala!") 25 | } 26 | } 27 | 28 | 29 | --------------------------------------------------------------------------------