Commencing the journey into Functional Programming in Scala has become notably simplified with the integration of Nix and Scala-cli-toolkit. This blog post is crafted to provide guidance and most of the commands that facilitate an easy transition from zero to proficient with Scala.
Introduction to Nix and Scala-cli
Nix Package Manager
Nix has emerged as a versatile package manager (it has been 20 years in the making) renowned for its innovative approach to dependency management and reproducible builds. Its declarative language and immutable infrastructure model offer developers control over their development environments. Nix stands out as a tool to empower teams to build, deploy, and manage software with efficiency and reliability.
Scala-cli Introduction
Scala-cli, an essential tool within the Scala ecosystem, streamlines the development process for Scala projects. Designed to enhance productivity and maintainability, Scala-cli provides developers with a unified platform for project management, dependency resolution, and build automation. By simplifying the setup and configuration of Scala projects, Scala-cli enables teams to focus on delivering high-quality code and innovative solutions without the burden of tedious manual tasks.
Dependencies: Just install Nix
We recommend following the installation guide from the source (https://nixos.org/download#nix-install-macos), but on Mac/Linux in one command
sh <(curl -L https://nixos.org/nix/install)
<a href="https://github.com/EstebanMarin/blogpost#scala-cli-introduction">Scala CLI introduction</a>
check the installation
❯ nix --version
nix (Nix) 2.19.3
For the final step, we will enable nix Flakes. The easiest, less permanent way to enable it is by adding the flag to the nix
command./nix
nix --experimental-features 'nix-command flakes'
Typelevel nix develop
With just one command (flakes need to be enabled, follow through the suggested experimental flags when/if an error is raised) nix develop github:typelevel/typelevel-nix#application
This will provide us with a complete development Scala environment with several tools (not exhaustive): Coursier, JVM, node, Gitter8, scala-fmt, scala-fix, scala-cli.
$ nix develop github:typelevel/typelevel-nix#application
🔨 Welcome to typelevel-app-shell
[general commands]
menu - prints this menu
sbt - A build tool for Scala, Java and more
scala-cli - Command-line tool to interact with the Scala language
[versions]
Java - 17.0.1
$ sbt -version
sbt version in this project: 1.9.8
sbt script version: 1.9.8
$ exit
<a href="https://github.com/EstebanMarin/blogpost#typelevel-nix-develop">Type level nix develop</a>
After exiting the shell, you will notice the underlying system has not changed and Scala
is no longer available.
Scala-CLI
Up to this point, we have a shell
with all the tooling necessary to be productive in Scala. Of those tools, we are going to explore scala-cli
and how to be functional easily with typelevel:toolkit
. First, let’s explore its main features.Scala CLI
Handle Scala versions, dependencies, and JVM with ease
Scala versions
❯ scala-cli run HelloWorld.scala
Compiling project (Scala 3.3.1, JVM (17))
Compiled project (Scala 3.3.1, JVM (17))
Hello, World!
❯ scala-cli --scala 2.13.6 HelloWorld.scala
Compiling project (Scala 2.13.6, JVM (17))
Compiled project (Scala 2.13.6, JVM (17))
Hello, World!<a href="https://github.com/EstebanMarin/blogpost#scala-versions">Scala Versions</a>
JVM versions
❯ scala-cli --jvm 8 HelloWorld.scala
Downloading JVM zulu:8
Compiling project (Scala 3.3.1, JVM (8))
Compiled project (Scala 3.3.1, JVM (8))<a href="https://github.com/EstebanMarin/blogpost#jvm-versions">JVM Versions</a>
Add dependencies to your REPL
❯ scala-cli --dep org.scalatest::scalatest:3.2.18
Welcome to Scala 3.3.1 (17.0.5, Java Java HotSpot(TM) 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> // scalatest available<a href="https://github.com/EstebanMarin/blogpost#add-dependencies-to-your-repl">Add dependencies to your repl</a>
There are many more features worth exploring, so visit the Scala-cli documentation for a full look.Handle scala versions dependencies and jvm with ease
Scripting 3 jobs in a functional way
We are going to write 3 files/scripts, and for that, we will need a basic vscode
metals lsp setup.Scripting 3 jobs in a functional way
$> scala-cil setup-ide .
$> touch HttpScript.scala
$> code .
Basic http4s client
//> using toolkit typelevel:0.1.21
//> using dependency org.http4s::http4s-dsl:0.23.25
//> using dependency org.http4s::http4s-ember-server:0.23.25
import cats.effect._
import cats.syntax.all._
import org.http4s._, org.http4s.dsl.io._, org.http4s.implicits._
import cats.effect._
import com.comcast.ip4s._
import org.http4s.HttpRoutes
import org.http4s.dsl.io._
import org.http4s.implicits._
import org.http4s.ember.server.EmberServerBuilder
object Http4sScript extends IOApp {
val helloWorldService = HttpRoutes
.of[IO] { case GET -> Root / "hello" / name =>
Ok(s"Hello, $name.")
}
.orNotFound
def run(args: List[String]): IO[ExitCode] =
EmberServerBuilder
.default[IO]
.withHost(ipv4"0.0.0.0")
.withPort(port"8080")
.withHttpApp(helloWorldService)
.build
.use(_ => IO.never)
.as(ExitCode.Success)
}
❯ scala-cli run Http4s.scala
Compiling project (Scala 3.3.1, JVM (17))
Compiled project (Scala 3.3.1, JVM (17))
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
# Separate shell
❯ http get localhost:8080/hello/xebia
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 13
Content-Type: text/plain; charset=UTF-8
Date: Sat, 17 Feb 2024 02:38:06 GMT
Hello, xebia.
Basic f2s streams
Also, run testsBasic f2s streams
//> using scala 3.3.1
//> using dep "org.scalamock::scalamock:6.0.0-M1"
//> using dep "org.scalatest::scalatest:3.2.18"
import org.scalamock.scalatest.MockFactory
import org.scalatest.funsuite.AnyFunSuite
class Example:
def method(input: String): String = "placeholder"
class MockTest extends AnyFunSuite with MockFactory:
test("Mocking Example.method") {
val example = mock[Example]
(example.method _).expects("input").returning("output")
assert(example.method("input") == "output")
}
❯ scala-cli test MockTest.scala
Compiling project (Scala 3.3.1, JVM (17))
Compiled project (Scala 3.3.1, JVM (17))
MockTest:
- Mocking Example.method
Run completed in 66 milliseconds.
Total number of tests run: 1
Suites: completed 1, aborted 0
Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0
All tests passed.
Better shell scripting
Scala is now a scripting language. We recommend reading Scala-cli is great for shell script.Better shell scripting
Summary
This blog primarily focuses on providing insights into setting up Scala and simplifying the process of creating practical applications. While we have covered a range of tools here, it is important to note that there is a wealth of information to delve into regarding each one. We aspire here to serve as a valuable resource for future reference, igniting inspiration, and encouraging further exploration and creation within the Scala ecosystem.Summary