Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
87: Mill as an sbt Alternative
Why should I even bother with Mill if sbt is the industry standard?
Look, sbt is incredibly powerful, but if you've spent any time with it, you know it can feel like a black box. You write a build.sbt file that looks like a weird hybrid of Scala and a custom DSL, and then you spend half your morning fighting with the shell or waiting for a reload that seems to take forever. I've been there.
Mill takes a different approach. Instead of a DSL, Mill builds are just Scala code. When you run a Mill command, it's essentially executing a Scala program that describes your build. This makes the build logic much more predictable and easier to debug. Plus, Mill is generally faster because it has a much more aggressive and reliable caching system. It doesn't try to "guess" what changed; it tracks the inputs to every single task. If the inputs haven't changed, it just gives you the cached result. Period.
What does a Mill build file actually look like compared to sbt?
In sbt, you have a flat build.sbt file. In Mill, you have a build.sc file. The biggest shift is that Mill uses objects to represent modules. Let's say we're building a small WeatherClient library that fetches data from an API. Here is how I'd structure that in Mill:
import mill._, scala.scale._
object weather extends ScalaModule {
def scalaVersion = "3.3.1"
def ivyDeps = Agg(
ivy"com.lihaoyi::upickleScala:3.1.0",
ivy"com.lihaoyi::nutype:0.12.0"
)
}
object weatherTest extends ScalaModule {
def scalaVersion = "3.3.1"
// This is where Mill shines: explicit dependencies between modules
def moduleDeps = Seq(weather)
def ivyDeps = Agg(
ivy"org.scalatest::scalatest:3.2.17"
)
}
Notice how weatherTest explicitly depends on weather? It's clean, it's type-safe, and you don't have to wonder where the dependency is coming from. You just define a module, tell Mill what it needs, and you're done.
How do I actually run things in Mill?
You'll find the CLI much more intuitive than the sbt shell. There is no "shell" you enter and stay in; you just run commands from your terminal. Mill uses a dot-notation that maps directly to the objects in your build.sc.
To compile your library, you'd run:
./mill mw compile
(Assuming you aliased weather to mw or just used the full name ./mill weather.compile). To run your tests, it's just as simple:
./mill weatherTest.test
I personally love that I can just use my standard zsh or bash aliases and piping without feeling like the build tool is trying to hijack my entire terminal experience. It feels like a tool, not an environment.
📋 Practical Task
Migrating the "FinancialCalculator" Project to Mill
You have a small project called FinancialCalculator that currently uses sbt. It consists of a main library and a test suite. Your goal is to migrate this to Mill to improve build times.
Current sbt configuration:
name := "FinancialCalculator"
version := "0.1"
scalaVersion := "3.3.1"
libraryDependencies += "org.typelevel" %% "cats-core" % "2.10.0"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.17" % Test
Your Task:
- Create a
build.scfile. - Define a
calcmodule that includes thecats-coredependency. - Define a
calcTestmodule that depends on thecalcmodule and includes thescalatestdependency. - Ensure both modules use Scala version
3.3.1.
Once you've written the file, verify that the calcTest module correctly references the calc module using moduleDeps.
There are no comments for now.