Skip to content

Commit

Permalink
Finalize control structures taste
Browse files Browse the repository at this point in the history
  • Loading branch information
jultty committed Nov 17, 2023
1 parent e429440 commit 1075688
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 25 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ def isTruthy(a: Matchable) = a match
case _ => true
```

```scala
val p = Person("Fred")

// later in the code
p match
case Person(name) if name == "Fred" =>
println(s"$name says, Yubba dubba doo")

case Person(name) if name == "Bam Bam" =>
println(s"$name says, Bam bam!")

case _ => println("Watch the Flintstones!")
```

> There’s much more to pattern matching in Scala. Patterns can be nested, results of patterns can be bound, and pattern matching can even be user-defined. See the pattern matching examples in the [Control Structures chapter](https://docs.scala-lang.org/scala3/book/control-structures.html) for more details.[^7]
### String interpolation
```scala
println(s"2 + 2 = ${2 + 2}") // "2 + 2 = 4"
Expand Down Expand Up @@ -281,6 +297,10 @@ Suppress `info` level logging when running and watching runs:
- <https://scalac.io/blog/scala-isnt-hard-how-to-master-scala-step-by-step/>
- <https://scalameta.org/munit/docs/getting-started.html>
- <https://scalameta.org/munit/docs/assertions.html>
- <https://www.baeldung.com/scala/sbt-scoverage-code-analysis>
- <https://scala-cli.virtuslab.org/>
- <https://typelevel.org/cats/>
- <https://zio.dev/>

## References
[^1]: <https://scalac.io/blog/why-use-scala/>
Expand All @@ -289,4 +309,4 @@ Suppress `info` level logging when running and watching runs:
[^4]: <https://docs.scala-lang.org/scala3/book/taste-vars-data-types.html>
[^5]: <https://docs.scala-lang.org/scala3/book/taste-control-structures.html>
[^6]: <https://scalameta.org/munit/docs/fixtures.html>

[^7]: <https://docs.scala-lang.org/scala3/book/taste-control-structures.html#match-expressions>
20 changes: 3 additions & 17 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,15 @@ lazy val root = project
.in(file("."))
.settings(
name := "scala-notes",
version := "0.1.0",
version := "0.2.0",
scalaVersion := scala3Version,

logLevel := Level.Warn,
run / watchLogLevel := Level.Warn,
test / watchLogLevel := Level.Warn,
test / watchBeforeCommand := Watch.clearScreen,
Global / onChangedBuildSource := ReloadOnSourceChanges,

scalacOptions ++= Seq(
"-unchecked",
"-explain",
"-Xprint-diff",
"-Xprint-diff-del",
"-Xprint-inline",
"-Ycheck-reentrant",
"-Ycook-comments",
"-Yprint-debug",
"-Yprint-pos-syms",
"-Yprint-debug-owners",
"-Yshow-var-bounds",
),
Global / cancelable := true,

libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test,

Expand All @@ -44,7 +31,6 @@ lazy val root = project
ContribWart.SealedCaseClass, ContribWart.SomeApply,
ContribWart.UnintendedLaziness,
),

wartremover.WartRemover.dependsOnLocalProjectWarts(customWarts),
wartremoverErrors ++= Seq(
Wart.custom("customWarts.CharPlusAny"), Wart.custom("customWarts.CharMinusAny"),
Expand All @@ -56,6 +42,6 @@ lazy val root = project
lazy val customWarts = project.in(file(".warts")).settings(
scalaVersion := scala3Version,
libraryDependencies ++= Seq(
"org.wartremover" % "wartremover" % wartremover.Wart.PluginVersion cross CrossVersion.full
"org.wartremover" % "wartremover" % wartremover.Wart.PluginVersion cross CrossVersion.full
),
)
145 changes: 138 additions & 7 deletions src/test/scala/Suite.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import scala.concurrent.duration.Duration
import scala.collection.mutable.ArrayBuffer

abstract class BaseSuite extends munit.FunSuite {
override val munitTimeout = Duration(10, "sec")
Expand All @@ -8,22 +9,152 @@ abstract class BaseSuite extends munit.FunSuite {
class FunFixture extends BaseSuite {

var on_setup: String = ""
var ints = ArrayBuffer.empty[Int]

val fixture = FunFixture[String](
setup = { _ => on_setup = "on setup"; on_setup },
val f = FunFixture[String](
setup = { _ => {
ints = ArrayBuffer(1, 2, 3, 4, 5)
on_setup = "on setup"
on_setup
}
},
teardown = { _ => on_setup = "" },
)

test("base suite is extended") {
f.test("base suite is extended") { _ =>
assertEquals(clue(on_base), "on base")
}

test(".fail test fails".fail) {
assertEquals(1, 0)
f.test("fixture setup context is set") { _ =>
assertEquals(clue(on_base), "on base")
assertEquals(clue(on_setup), "on setup")
}

fixture.test("fixture check is set during setup") { _ =>
assertEquals(clue(on_setup), "on setup")
f.test(".fail test fails".fail) { _ =>
assert(false)
}

f.test("control structures are expressions") { _ =>
val is = if 1 > 0 then "greater" else "less"
assertEquals(clue(is), clue("greater"))
}

f.test("for loop generator") { _ =>
var ints2 = ArrayBuffer.empty[Int]
for i <- ints do ints2 += i
assertEquals(clue(ints2), clue(ArrayBuffer(1, 2, 3, 4, 5)))
}

f.test("for loop guards") { _ =>
var greater = ArrayBuffer.empty[Int]
for i <- ints
if i > 3
do
greater += i
assertEquals(clue(greater), clue(ArrayBuffer(4, 5)))
}

f.test("for with multiple guards and generators") { _ =>

var last_i = -1
var last_j = 'Z'

for
i <- 1 to 3
j <- 'a' to 'c'
if i == 2
if j == 'b'
do
last_i = i
last_j = j

assertEquals(clue(last_i), clue(2))
assertEquals(clue(last_j), clue('b'))
}

f.test("for yield returns the same data structure with results") { _ =>
val doubles = for i <- ints yield i * 2
assertEquals(doubles, ArrayBuffer(2, 4, 6, 8, 10))
}

f.test("for can have multiple yield expressions") { _ =>

val results = ArrayBuffer.empty[Int]

val result = for {
int <- ints if int % 2 == 0
} yield {
results += int
val square = int * int
results += square
results += square * 2
}

assertEquals(clue(results), clue(ArrayBuffer(2, 4, 8, 4, 16, 32)))
}

f.test("basic match expression") { _ =>
val i = 3
var number = ""

i match
case 1 => number = "one"
case 2 => number = "two"
case 3 => number = "three"
case _ => number = "other"

assertEquals(clue(number), "three")
}

f.test("match expressions return values") { _ =>
val i = 2

val number = i match
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "other"

assertEquals(clue(number), "two")

}

f.test("match expressions can match types") { _ =>
def matchType(x: Matchable): String = x match
case s: String => s"String containing: $s"
case i: Int => "Int"
case d: Double => "Double"
case l: List[?] => "List"
case _ => "Unexpected type"

assertEquals(clue(matchType("a string")), clue("String containing: a string"))
assertEquals(clue(matchType(4.92)), clue("Double"))
assertEquals(clue(matchType(List(3, 2, 1))), clue("List"))
}

f.test("division by zero throws ArithmeticException") { _ =>

var always: String = ""

val exception = try
2/0
catch
case nfe: NumberFormatException => "Got a NumberFormatException"
case nfe: ArithmeticException => "Got an ArithmeticException"
finally
always = "This always executes"

assertEquals(clue(exception), clue("Got an ArithmeticException"))
assertEquals(clue(always), clue("This always executes"))
}

f.test("while loop with block return") { _ =>
val result = {
var x = 0
while x < 13 do x += 3
x
}
assertEquals(clue(result), 15)
}
}

0 comments on commit 1075688

Please sign in to comment.