Skip to content

Commit b102300

Browse files
committed
started Jetbrains Scala
1 parent ab68c9f commit b102300

File tree

186 files changed

+4025
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

186 files changed

+4025
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ hs_err_pid*
4747
.tmcproject.yml
4848
.tmcproject.json
4949
course_config.toml
50+
*.yaml

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Updated November 2023 (C for Everyone)
2626

2727
Updated February 2024 (Semantics of First Order Logic)
2828

29+
Updated March 2024 (Jetbrains Academy Functional Scala)
30+
2931
- [Learning tips](#tips)
3032
- [Harvard's CS50 (first half)](#cs50)
3133
- [CS50, second half, Final Project](#cs50-2)
@@ -63,6 +65,7 @@ Updated February 2024 (Semantics of First Order Logic)
6365
- [Kotlin for Java Developers](#kotlin)
6466
- [C for Everyone (non-review)](#c4e)
6567
- [Semantics of First Order Logic](#tarski)
68+
- [Jetbrains Academy Functional Scala](#fpScala)
6669

6770
### <a name="tips"></a> Learning tips
6871

@@ -1125,3 +1128,21 @@ Of couse the course stops short of "real" logic such as syntactic proofs, or qua
11251128
One minor complaint is that some of the handwritten parts in the videos are too small and difficult to read.
11261129

11271130
*Spam's recommendation:* Very nice! Finally a decent logic course. Take it, it's fairly short; but you have to finish it *very quickly* within 1 month, since you lose all audit access. Good thing it's short. You'll still have to learn symbolic formal proofs elsewhere, but this is the right starting point on your logic journey. (I'd like to recreate elements of this with [Doodle](https://github.com/creativescala/doodle/) in Scala for my own curriculum one day.)
1131+
1132+
### <a name="fpScala"></a> Jetbrains Academy Functional Scala
1133+
1134+
Another "inside your IDE" course, maybe more like a tutorial? It's text only. It assumes Scala knowledge already. It's impossible to find Scala learning materials for beginners. You need to either know Java, or Scala, or have lots of experience somehow.
1135+
1136+
Poor first impressions: file tabs use red colors for file names, which makes it look like there are errors in the file. For some reason all the files are indented using... *3 spaces?* And "reindent file with 2 spaces" does not work. In fact, after I manually edit the file to use 2 spaces, "reindent file with 2 spaces" reindents the file with... 3 spaces! Moreover, the course is still using Scala 2 code, with `object Main` containing old-school C-style `def main(args: Array[String])`, all of which are wrapped with lots of curly braces. And no worksheets... Come on Jetbrains, clean up your Scala 3 game already... It improves later with using colon `:` and Python-style indentation instead of curly braces.
1137+
1138+
Super basic things: what is a function? `val` vs. `def`. Then there are exercises of the "write a *very* slightly different version of functions in the text" type. You can copy/paste the code from the text, and change 1-2 things, done. They are checked directly inside the IDE.
1139+
1140+
There are links to "additional materials" with links to... *blog posts* (come on...) and videos on Youtube explaining the subjec, which can be viewed directly inside the IDE! But if you click on the "Youtube" button, it opens a Chrome-like browser window that runs... ADS! Like WTF?! Then, why aren't the videos included by default to make it a *proper course*? This is just... so weird. They made videos to go with the material but hid them behind "additional materials" that open in a quasi-browser window with ads. Jetbrains has an extremely strange way of doing things. I really don't understand. I don't think these are deliberate, they are just awkward nerds probably.
1141+
1142+
After some super baby stuff, things ramp up quite fast. The difficulty curve is way off... Ironically the IDE asks you to fill a "Computer Science learning curve" survey! We are told to "go read Scala documentation for collection methods" like `filter`, `map` etc. Then there is a big assignment with a ton of `enum` hierarchies. A `Cat` has a `Pattern` which has a `TriColor` which has a `TricolorSubtype` which can be one of `Calico` or `Tortie`. Similarly for other attributes of a `Cat`. Gotta track them through hierarchies. They didn't even make use of Scala's `???` for leaving things unimplemented for now but still compiling. So you have to fill all answers before you can check. Then exercises about the collection methods. `find`, `foreach`, `map`, `flatMap`, `foldLeft`. Difficulty again raises quite a lot here, especially with `foldLeft`.
1143+
1144+
Following sections are about pattern matching, immutability, expressions. That's it. It's all well and good... but again it all reeks of asking you to do stuff without really properly teaching. That's part of the "go fast, kinda-sorta guess stuff, skip over some stuff, Google stuff, look up blogs, and muddle through" style "learning" culture in the software industry I guess... they just don't have the time. What can you do...
1145+
1146+
IntelliJ was using *so much RAM* (more than 4GB) that my operating system started killing some apps to free some memory (`oom-killer`?). Some of the assignment instructions have typos, so they ask you to do wrong things, which then fails the tests. It's possible to peek at the solutions to see what is wrong. Some exercises are weirdly implemented. Like the typical "find max of a list" exercise. Normally you have to check for an empty list, and throw an exception. But not here... you have to use `Int.MinValue` apparently!
1147+
1148+
*Spam's recommendation:* A bit pointless, and not of high quality (similar to the Kotlin course). It's OK I guess. You can skip it. If you are familiar with Scala, you would know all of this already. Maybe it's good for those coming from Java? I don't think so; since it does not cover Scala basics; they would not know what a `val` is! So, it does not do a good job of filling those gaps, but also of offering something good and new to Scala users either.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
scalaSource in Compile := baseDirectory.value / "src"
2+
scalaSource in Test := baseDirectory.value / "test"
3+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Main {
2+
def main(args: Array[String]): Unit = {
3+
// Write your solution here
4+
}
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Feedback survey
2+
3+
Thank you for taking our Functional Programming in Scala course!
4+
We would really appreciate it if you could take a few minutes to
5+
answer [our survey](https://surveys.jetbrains.com/s3/course-feedback-functional-programming-scala).
6+
Your feedback will help us improve this course and make it relevant for future students.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
scalaSource in Compile := baseDirectory.value / "src"
2+
scalaSource in Test := baseDirectory.value / "test"
3+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15"
4+
scalaVersion := "3.2.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
object NestedTask:
2+
object Task1:
3+
def f(): Unit =
4+
def g(): Unit =
5+
h()
6+
def i(): Unit =
7+
h()
8+
def h(): Unit = ()
9+
10+
object Task2:
11+
def f(x: Int): Unit =
12+
h()
13+
g(42)
14+
def g(y: Int): Unit =
15+
def h(z: Int): Unit = ()
16+
h(x+y)
17+
def h(): Unit =
18+
g()
19+
def g(): Unit = ()
20+
21+
def f(x: Int, y: Int): Int =
22+
def g(z: Int): Int =
23+
def h(): Int =
24+
x + y + z
25+
h()
26+
// def i(): Int = z // z is not visible outside g
27+
g(42)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
## Nested Methods
2+
3+
In Scala, it's possible to define methods within other methods.
4+
This is useful when you have a function that is only intended for one-time use.
5+
For example, you may wish to implement the factorial function using an accumulator to enhance the program's efficiency.
6+
Simultaneously, you would not want to allow the user to call the function with an arbitrary accumulator parameter.
7+
In this situation, you can expose a standard one-parameter function `factorial`, which calls the nested tail-recursive implementation
8+
`fact` with the appropriate accumulator:
9+
10+
```scala 3
11+
def factorial(x: Int): Int =
12+
def fact(x: Int, accumulator: Int): Int =
13+
if x <= 1 then accumulator
14+
else fact(x - 1, x * accumulator)
15+
fact(x, 1)
16+
```
17+
18+
An alternative option is to place the `fact` function on the same level as `factorial` and make it `private`.
19+
This still permits other functions in the same module to access `fact`, whereas nesting it renders it exclusively accessible
20+
from inside the `factorial` function.
21+
You can also have nested methods within other nested methods, with the rules of scoping being consistent: the nested function is
22+
only accessible from within its outer function:
23+
24+
```scala 3
25+
def foo() = {
26+
def bar() = {
27+
def baz() = { }
28+
baz()
29+
}
30+
def qux() = {
31+
def corge() = { }
32+
corge() // A nested function can be called
33+
bar() // A function on the same level can be called
34+
// A function nested within the other function cannot:
35+
// baz() // not found: baz
36+
}
37+
// Functions on this level can be called...
38+
bar()
39+
qux()
40+
// ... but their nested functions cannot:
41+
// baz() // not found: baz
42+
// corge() // not found: corge
43+
}
44+
```
45+
46+
Note that we've used curly braces to delineate scopes more clearly; however, they are not needed in Scala 3.
47+
48+
Nested functions can access the parameters of their parents, so you can avoid passing parameters that do not change:
49+
50+
```scala 3
51+
def f(x: Int, y: Int): Int =
52+
def g(z: Int): Int =
53+
def h(): Int =
54+
x + y + z
55+
h()
56+
// def i(): Int = z // z is not visible outside g
57+
g(42)
58+
```
59+
60+
Another instance where nested methods prove particularly useful is when you create a chain of calls to higher-order
61+
functions, utilizing nested methods to assign meaningful names to their arguments.
62+
Consider the example where we count the number of kittens that are either white or ginger.
63+
64+
```scala 3
65+
enum Color:
66+
case Black
67+
case White
68+
case Ginger
69+
70+
// We have a model in which any cat has a color and an age (in years)
71+
class Cat(val color: Color, val age: Int)
72+
73+
val bagOfCats = Set(Cat(Color.Black, 0), Cat(Color.White, 1), Cat(Color.Ginger, 3))
74+
75+
// Count the number of white or ginger kittens (cats that are not older than 1 year)
76+
val numberOfWhiteOrGingerKittens =
77+
def isWhiteOrGinger(cat: Cat): Boolean = cat.color == Color.White || cat.color == Color.Ginger
78+
def isKitten(cat: Cat): Boolean = cat.age <= 1
79+
bagOfCats.filter(isWhiteOrGinger).count(isKitten)
80+
```
81+
82+
We could have written the latter function as shown below, but it is obviously less readable:
83+
84+
```scala 3
85+
val numberOfWhiteOrGingerKittens =
86+
bagOfCats
87+
.filter(cat => cat.color == Color.White || cat.color == Color.Ginger)
88+
.count(cat => cat.age <= 1)
89+
```
90+
91+
### Exercise
92+
93+
Explore the scopes of the nested methods. Make the code in the file `NestedTask.scala` compile.

courses/fpScala/Expressions over Statements/Nested Methods/test/NestedTaskSpec.scala

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
scalaSource in Compile := baseDirectory.value / "src"
2+
scalaSource in Test := baseDirectory.value / "test"
3+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15"
4+
scalaVersion := "3.2.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import scala.io.StdIn
2+
3+
object PureVsImpureTask:
4+
var logs: List[String] = List.empty
5+
def calculateAndLogImpure(data: List[Int]): Int =
6+
// Modifies global state
7+
logs ::= s"Received data of size: ${data.size}"
8+
val result = data.sum
9+
// Modifies global state
10+
logs ::= s"Calculated sum: $result"
11+
result
12+
13+
def calculateAndLogPure(data: List[Int], logs: List[String]): (Int, List[String]) =
14+
/* TODO */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
## Pure vs Impure Functions
2+
3+
Not all functions are created equal; some of them are better than others.
4+
A large group of such superior functions are designated as *pure*.
5+
A pure function always yields the same value if given the same inputs.
6+
For example, the mathematical function `double(x) = 2 * x`, for doubling a number, always returns `42` when given `21` as an
7+
argument.
8+
Conversely, the function `g`, which takes a number as an argument, reads another from the standard input, and then multiplies them,
9+
does not always compute the same result when called with `21`.
10+
11+
```scala 3
12+
def g(x: Int): Int =
13+
val y = StdIn.readInt()
14+
x * y
15+
16+
println(g(21)) // Input: 1 => printed 21
17+
println(g(21)) // Input: 3 => printed 63
18+
```
19+
20+
Another consequence of having to always produce the same result is that a pure function cannot perform any side effects.
21+
For instance, a pure function cannot output anything, interact with a database, or write into a file.
22+
It cannot read from the console, database, or a file, modify its arguments, or throw an exception.
23+
The result solely depends on the arguments and the implementation of the function itself.
24+
Its performance should neither be influenced by the external world nor impact it.
25+
26+
You might argue that pure functions seem entirely useless.
27+
If they cannot interact with the outer world or mutate anything, how is it possible to derive any value from them?
28+
Why even use pure functions?
29+
The fact is, they conform much better than impure counterparts.
30+
Since there are no hidden interactions, it's much easier to verify that your pure function does what it
31+
is supposed to do and nothing more.
32+
Moreover, they are much easier to test, as you do not need to mock a database if the function never interacts with one.
33+
34+
Some programming languages, such as Haskell, restrict impurity and reflect any side effects in types.
35+
However, it can be quite restricting and is not an approach utilized in Scala.
36+
The idiomatic method is to write your code in such a way that the majority of it is pure, and impurity is only used
37+
where it is absolutely necessary, similar to what we did with mutable data.
38+
For example, the function `g` can be split into two: the one that reads the number from the standard input and the one
39+
that is responsible for multiplication:
40+
41+
```scala 3
42+
def gPure(x: Int, y: Int): Int =
43+
x * y
44+
45+
def g(x: Int): Int =
46+
val y = StdIn.readInt()
47+
gPure(x, y)
48+
```
49+
50+
### Exercise
51+
52+
Implement the pure function `calculateAndLogPure` which does the same thing as `calculateAndLogImpure`, but without
53+
using global variable.

courses/fpScala/Expressions over Statements/Pure vs Impure Functions/test/PureVsImpureTaskSpec.scala

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
scalaSource in Compile := baseDirectory.value / "src"
2+
scalaSource in Test := baseDirectory.value / "test"
3+
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.15"
4+
scalaVersion := "3.2.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import scala.annotation.tailrec
2+
3+
object RecursionTask:
4+
enum Operation:
5+
case Plus
6+
case Mult
7+
8+
enum Tree:
9+
case Node(val operation: Operation, val left: Tree, val right: Tree)
10+
case Leaf(val num: Int)
11+
12+
import Tree.*
13+
import Operation.*
14+
15+
def eval(ast: Tree): Int =
16+
/* TODO */
17+
18+
def prefixPrinter(ast: Tree): String =
19+
/* TODO */
20+
21+
@main
22+
def main(): Unit =
23+
/**
24+
* *
25+
* / \
26+
* + 5
27+
* / \
28+
* 1 3
29+
*/
30+
val tree = Node(Mult, Node(Plus, Leaf(1), Leaf(3)), Leaf(5))
31+
println(eval(tree)) // 20
32+
println(prefixPrinter(tree)) // * + 1 3 5
33+

0 commit comments

Comments
 (0)