Last updated on
Degrees converter
The goal of this lab is to familiarize yourself with the tools and environment of this class. Even though the grade in this lab won’t count towards your final score, it’s essential to complete this lab carefully.
Obtaining the lab files
Start by ensuring you’ve completed the Tools Setup instructions.
Then, clone the lab repository:
$ git clone https://gitlab.epfl.ch/lamp/cs-214/degrees.git
For now, do not worry about what the git clone
command does. We will cover this in a later lecture.
The $
is called the prompt and isn’t part of the command. It’s a convention used to indicate that you should type the text after the $
sign and then press the Enter or ⏎ key to execute the command.
This will create a directory named degrees
containing the lab project files. You can change your working directory by running the change directory (cd
) command:
$ cd degrees
Now that we’ve obtained the lab, let’s take a look at its structure:
.
├── build.sbt
├── project
│ └── ...
└── src
├── main
│ └── scala
│ ├── degrees
│ │ ├── cli
│ │ │ └── main.scala
│ │ └── convert.scala
│ └── examples.worksheet.sc
└── test
└── scala
└── degrees
└── ConvertTest.scala
- All the files ending with
.sbt
or in theproject/
directory are build tool configuration files: you don’t need to modify them yet. We will discuss them later in the course. - The lab sources are in
src/main/scala/
. - The sources of the unit tests are in
src/test/scala/
. You will need to make all the tests pass to complete the labs, and you should write additional tests to check for cases that our tests do not cover.
Using SBT and running the tests
SBT stands for Scala Build Tool, it’s a dependency manager and build tool for Scala. We will use it to compile our code and run the tests.
Learn more about SBT
On one hand, SBT is a dependency manager, allowing developers to specify and fetch libraries their code relies upon. This ensures consistent and reproducible builds, this is similar, for example, to how NPM handles JavaScript dependencies. On the other hand, SBT also functions as a build tool, like the classic ‘Make’ for C/C++. It can compile Scala code, run tests, and package applications into deployable units.
There are other build tools for Scala—such as Scala CLI or Mill, that are worth checking out— but SBT is currently the most popular one, and the one we will use in this course.
At the root of the project, you’ll find a file named build.sbt
. This file is
used by SBT to know how to build your project. You don’t need to modify it or to
understand it for now, but you can take a look at it if you’re curious. You will
see that it contains three lines: the first line defines the name of the
project, the second line defines the version of Scala we are using, and the
third line adds a dependency on MUnit, a library
that we will use for testing.
Start SBT by running the following command in the project’s root directory (the
directory that you cloned from git and that contains the build.sbt
file):
$ sbt
Once it’s finished starting (this may take a while), you’ll be able to enter SBT commands:
- You can compile your project using the
compile
command. - You can run the tests with the
test
command (this automatically compiles your code if needed to). Note that if compilation fails, no tests will be run. - You can exit SBT using the
exit
command.
The first time you’ll run test
in a lab you should see many errors: that’s
normal, your job is to make the tests pass!
Let’s look at the output of the test
command in more details:
$ sbt
[info] welcome to sbt 1.10.1 (Eclipse Adoptium Java 17.0.6)
[info] loading settings for project degrees-build-build-build from metals.sbt ...
⋮ [some lines omitted]
[info] started sbt server
sbt:degrees> test
[info] compiling 2 Scala sources to /Users/mbovel/degrees/target/scala-3.3.1/classes ...
[info] compiling 1 Scala source to /Users/mbovel/degrees/target/scala-3.3.1/test-classes ...
degrees.ConvertTest:
+ celsiusToFahrenheit(0) should be 32 (1pt) 0.007s
+ celsiusToFahrenheit(100) should be 212 (1pt) 0.001s
==> X degrees.ConvertTest.fahrenheitToCelsius(32) should be 0 (1pt) 0.012s munit.ComparisonFailException: /Users/mbovel/degrees/src/test/scala/degrees/ConvertTest.scala:13 values are not the same expected: 0.0 but was: 42.0
12: test("fahrenheitToCelsius(32) should be 0 (1pt)"):
13: assertEqualsDouble(fahrenheitToCelsius(32), 0.0, DELTA)
14:
at munit.Assertions.failComparison(Assertions.scala:274)
==> X degrees.ConvertTest.fahrenheitToCelsius(212) should be 100 (1pt) 0.0s munit.ComparisonFailException: /Users/mbovel/degrees/src/test/scala/degrees/ConvertTest.scala:16 values are not the same expected: 100.0 but was: 42.0
15: test("fahrenheitToCelsius(212) should be 100 (1pt)"):
16: assertEqualsDouble(fahrenheitToCelsius(212), 100.0, DELTA)
17:
at munit.Assertions.failComparison(Assertions.scala:274)
==> X degrees.ConvertTest.celsiusToFahrenheit and fahrenheitToCelsius should be inverse functions (2pts) 0.003s munit.ComparisonFailException: /Users/mbovel/degrees/src/test/scala/degrees/ConvertTest.scala:20 values are not the same expected: -100.0 but was: 42.0
19: for v <- -100 to 100 do
20: assertEqualsDouble(fahrenheitToCelsius(celsiusToFahrenheit(v)), v.toDouble, DELTA)
21: assertEqualsDouble(celsiusToFahrenheit(fahrenheitToCelsius(v)), v.toDouble, DELTA)
at munit.Assertions.failComparison(Assertions.scala:274)
[error] Failed: Total 5, Failed 3, Errors 0, Passed 2
[error] Failed tests:
[error] degrees.ConvertTest
[error] (Test / test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 4 s, completed Sep 15, 2023, 7:51:51 PM
This tells us several things:
- The tests run are from the class
ConvertTest
in thedegrees
package. - The first two tests passed successfully.
- The last three tests failed. That’s why they are displayed in red and
prefixed with
==> X
. - The failures were due to
munit.ComparisonFailException
errors.
You’ll need to go ahead and implement the fahrenheitToCelsius
function to fix
these test failures!
Starting VS Code
(If you are still inside SBT, exit it by entering exit
at the SBT prompt.)
To start VS Code, run the following in the project directory (the same directory
where you previously ran sbt
).
$ code .
In the terminal, a .
signifies the current directory. It is important to include in the command above so that VS Code runs in the correct directory and is able to import the project.
If you see an error message, “Expected ID character […]”, it means you’re still inside SBT. You can
exit SBT by typing exit
and pressing Enter or ⏎.
The first time the IDE starts, it will take some time to download more components. Wait until it asks you to import the build, and then click “Import build”:
You’ll need to wait a bit more for the import to finish. If an error occurs, try closing and restarting VS Code in the same way you started it above.
Digging further
It’s now time to dig in! Earlier, we saw a failing test. The stack trace
told us that it was failing on line 10 of the file
src/test/scala/degrees/ConvertTest.scala
.
Here is the source code of the failing test:
test("fahrenheitToCelsius(32) should be 0 (1pt)"):
assertEqualsDouble(fahrenheitToCelsius(32), 0.0, DELTA)
src/test/scala/degrees/ConvertTest.scala
The first line gives a name to the test. The second line runs
fahrenheitToCelsius(32)
and tests that it equals 0.0
(up to some delta,
because we are dealing with floating-point numbers), but in our case we never
got to this point because an exception was thrown. Recall that the second line
of the stack trace was:
==> X degrees.ConvertTest.fahrenheitToCelsius(32) should be 0 (1pt) 0.012s munit.ComparisonFailException: /Users/mbovel/degrees/src/test/scala/degrees/ConvertTest.scala:13 values are not the same expected: 0.0 but was: 42.0
This tells us that the exception was thrown when calling the
fahrenheitToCelsius
while testing the output of fahrenheitToCelsius(32)
, in
the ConvertTest.scala
file at line 13.
We can hover over the call to fahrenheitToCelsius
in the test
method for further information:
If hovering doesn’t show this, see the Troubleshooting section.
The hover text has two lines. The first line is:
def fahrenheitToCelsius(fahrenheit: Double): Double
This means that fahrenheitToCelsius
is a method that takes a Double
argument
and returns a Double
.
The second line is the documentation of
fahrenheitToCelsius
. We can jump to the definition of fahrenheitToCelsius
by clicking on it while pressing
Ctrl (or Cmd on Mac), or by right clicking on it and selecting “Go to Definition”. There, we see:
/** Converts a temperature in Fahrenheit degrees to Celsius */
def fahrenheitToCelsius(fahrenheit: Double): Double =
42.0
src/main/scala/degrees/convert.scala
Now we know why the test failed: fahrenheitToCelsius
always returns 42.0
!
Once you’ve fixed this method, you can run test
from SBT again to see if
the test passed.
If you want to run a single test instead of all tests, you can use the
testOnly
command in the SBT shell to match on the name of the test. For
example:
sbt:degrees> testOnly -- "*fahrenheitToCelsius(32)*"
This will match and run the single test
degrees.ConvertTest.fahrenheitToCelsius(32) should be 0 (1pt)
. (The first *
means anything can appear before fahrenheitToCelsius(32)
and the second *
means anything can appear after.)
You now know enough to be able to work with the IDE. Here are some additional tips:
-
When working on an lab, you are free to create as many methods, classes and objects as you want. But do not change the name of existing methods, classes, and objects, because that may break the automated grading system!
-
You can customize VS Code as much as you want, including installing additional extensions, but please avoid installing other Scala-related extensions: they may conflict with the one we use for this course.
-
While working on your lab, you will regularly want to go back to the SBT console to run the tests. You could simply run the command
test
every time, but you can take advantage of the watch mode instead: if a command is prefixed by~
, SBT will watch the source directory for changes and re-run the command every time a file is saved. So, a possible workflow is:- Start the IDE
- Start SBT in the terminal (protip: you can start a terminal inside VS
Code from the menu:
Terminal -> New Terminal
) - Inside SBT, run
~test
- Work in the IDE
- Every time you want to check the SBT output, save the files you’ve modified so that
test
is re-run automatically
Running your code
Writing code and running tests is nice, but sometimes more direct feedback is useful, like when you want to experiment with Scala, or try out some methods that you implemented. You can do this with a worksheet, the Scala REPL, or a main
function.
The worksheet mode
A worksheet is a file where every line of code written in the IDE is executed and its output displayed as a comment. Any file that ends in .worksheet.sc
in src/main/scala
is considered to be a worksheet by the IDE.
There is a worksheet in this example project at src/main/scala/degrees/examples.worksheet.sc
. You can open it by clicking on it in the file explorer on the left.
Inside this file, you can type any Scala code. The worksheet will be automatically run when the code is saved. Each line of code will be executed one by one and its output will be shown in green on the right.
If your IDE is configured correctly, you should see the following initial output:
If no output is shown, see the Troubleshooting section, and ask a member of the teaching staff for help if you can’t get it to work.
The REPL
If you are more of a terminal person, you can also use the Scala REPL for similar purposes. REPL stands for Read-Eval-Print-Loop, it’s a program that reads Scala expressions, evaluates them, prints the result, and then loops back to read the next expression.
After having started SBT, you can start the REPL by typing console
, you will see the following prompt:
scala>
At this point you can write any Scala expression you want, for example:
scala> val x = 41 + 1
val x: Int = 42
If you write an expression without wrapping it in a val
or a def
, the REPL will give it a name for you, starting with res
:
scala> 41 + 1
val res0: Int = 42
scala> 41.5 + 0.5
val res1: Double = 42.0
scala> "Hello".isBlank
val res2: Boolean = false
The functions and classes of the lab are available inside the REPL, so you can, for instance, import all the methods fahrenheitToCelsius
from the degrees
package by typing:
scala> import degrees.fahrenheitToCelsius
scala> fahrenheitToCelsius(0.0)
val res4: Double = 42.0
You can enter a multi-line expression in the REPL by using Alt+Enter at the ends of lines instead of Enter:
scala> if 1 == 1 then
| "a"
| else
| "b"
val res5: String = a
In order to exit the Scala REPL and go back to SBT, type :quit
or press Ctrl+D.
Running a main function
The last way to run your code is to run a main function. A main function is a method designed to be called from the command line. It is similar to a main
method in Java. We will come back to this later in the course.
In this lab, there is a main called main
defined for you in the cli
package. You do not need to understand or modify this code yet.
However, know that you can run this main function by typing run
in the SBT console, followed by the arguments expected by this method. In this lab, the main function defined for you expects two arguments: the first one is the temperature to convert, and the second one is the unit of the temperature to convert. For example:
sbt:degrees> run 30 c
[info] running degrees.main 30 c
30°C = 86.00°F
[success] Total time: 1 s, completed Sep 14, 2023, 7:46:27 PM
sbt:degrees> run 20 f
[info] running degrees.main 20 f
20°F = 42.00°C
[success] Total time: 0 s, completed Sep 14, 2023, 7:47:32 PM
If you run the method with the wrong number of arguments, or with invalid arguments, you will get an error:
sbt:degrees> run
[info] running degrees.main
Illegal command line: more arguments expected
sbt:degrees> run 40 m
[info] running degrees.main 40 m
[error] java.lang.IllegalArgumentException: Unknown unit m
[error] at degrees.main$package$.main(main.scala:9)
...
Submitting your work
If all the tests run successfully, then you are done—congratulations!
To turn in your lab, submit the convert.scala
file (and only this file) to Moodle.
On the assignment page:
- Click “Add submission”.
- Drop the
convert.scala
file in the “File submissions” box. - Click “Save changes”.
- Click “Submit assignment”.
- Confirm that you respected the rules by checking the box next to “This assignment is my own work”.
- Click “Continue”.
Double-check that:
- Your submission status is “Submitted for grading”.
- You have submitted the
convert.scala
file, and only this file. - You have received a confirmation email with the subject “You have submitted your assignment submission for Lab: Degrees”.
Troubleshooting
SBT fails to start
If you see any kind of error when SBT starts that prevents you from using it, try cleaning the project cache by running:
$ git clean -Xdf
(To understand this command, consult git clean documentation.)
Then, restart SBT. If this still doesn’t work, try deleting the global SBT cache:
-
On Windows, navigate to
C:\Users\%USERNAME%
and delete the.sbt
folder. -
On GNU/Linux or macOS:
$ rm -r ~/.sbt
Make sure to not put any spaces in the
~/.sbt
path. To see the documentation ofrm
, try the commandman rm
.
IDE features like hover text, go-to-definition, or worksheets do not work
It’s likely that the build wasn’t imported. Try importing it manually: click on the “m” logo in the left bar, and then in the sidebar that appears, click on “Import build”, and then wait a bit.
If things still don’t work, try restarting VS Code (launch it in the same way
you started it before, using code .
from the project directory). If you’re
still having issues, try clicking on “Clean compile workspace” from the same
sidebar.
Import errors for external packages
Some of our labs use advanced features of SBT. If you try to do these labs with the default VS Code configuration, you may run into issues, such as missing completions or import errors. These issues will not prevent the code from compiling and running correctly, because they only concern the IDE support for Scala, not the project itself. IDE support is provided by software called Metals.
To fix these issues, you can ask Metals to talk to SBT directly, rather than using the default build tool (called “Bloop”). To do this just once:
- Press Ctrl+Shift+P (“Show All Commands”).
- Type “metals switch” and select the item “Metals: Switch build server”.
- When prompted, choose “SBT” instead of “Bloop”.
To make this switch permanent, you can either add "defaultBspToBuildTool": true
to VS Code’s settings.json
(at either the project level or for all projects), or click the gears icon to open the settings menu and search for “metals bsp” to find the corresponding setting in the UI.
Note: It’s not safe to run multiple copies of SBT in parallel. So, if you configure Metals to use SBT as above, then to run your code or tests, start the console, etc. using SBT at the command line, you should use sbt --client
instead of just sbt
. If nothing appears when you type commands into SBT, try sbt -Djline.terminal=none --client
instead.
Last resort
If you’re still having issues, come to a lab or exercise session and ask a staff member for help.