├── .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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 |
112 |
113 |
114 |
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 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/scala_compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/scala_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
--------------------------------------------------------------------------------