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
22: Currying in Scala
When I first started with Scala, I fell into a trap that almost every functional programmer hits: I thought currying was just a fancy way of writing multiple parameter lists to make the code look "more academic." I figured it was just a syntactic preference, like choosing between a for loop and a while loop. I was wrong.
"It's just a different way to write parentheses"
The misconception is that def add(x: Int)(y: Int) is functionally identical to def add(x: Int, y: Int). On the surface, when you call add(1)(2), you get the same result as add(1, 2). But this ignores the fundamental shift in what the function is.
// Standard function: Takes two Ints, returns one Int
def multiply(x: Int, y: Int): Int = x * y
// Curried function: Takes one Int, returns A NEW FUNCTION that takes one Int
def curriedMultiply(x: Int)(y: Int): Int = x * y
In the first example, you have a single jump from input to output. In the second, you've created a factory. If I call curriedMultiply(10), the program doesn't throw an error for missing an argument; instead, it returns a function that "remembers" the number 10 and is just waiting for the second number to finish the job. That's the core of currying: transforming a function that takes $n$ arguments into a chain of $n$ functions that each take a single argument.
Turning a general tool into a specialized one
Now, why would you actually do this in a production codebase? The real power comes when you combine currying with partial application. I often use this when I have a set of configuration parameters that stay the same for a while, followed by the actual data that changes constantly.
Imagine we're building a system to calculate shipping costs based on a carrier's base rate and a package weight. Instead of passing the base rate every single time, we curry the function:
def calculateShipping(baseRate: Double)(weight: Double): Double = {
baseRate * weight
}
// I can now "lock in" the rate for FedEx
val fedExShipping = calculateShipping(15.50)
val upsShipping = calculateShipping(12.00)
// Now I have specialized functions for specific carriers
val cost1 = fedExShipping(2.5) // Only need to provide the weight now
val cost2 = upsShipping(2.5)
I've essentially used currying to create a "template" for shipping. This keeps the business logic (the multiplication) separate from the configuration (the rates), and it makes your call sites much cleaner.
The secret weapon: Type Inference
If the "specialization" argument doesn't convince you, the type system will. This is where currying becomes non-negotiable in Scala. Scala's type inference works from left to right. In a standard function, the compiler tries to infer the types of all parameters at once.
But with curried functions, the compiler can infer the type of the first block of parameters and use those types to determine the requirements for the second block. You'll see this everywhere in the Scala standard library, especially with map or implicit parameters. If we had one giant list of parameters, the compiler often wouldn't be able to "guess" the type of the second argument based on the first, forcing you to write tedious, explicit type annotations everywhere. Currying gives the compiler a chance to breathe and figure things out for you.
📋 Practical Task
Build a Dynamic Discount Engine
You are tasked with creating a flexible pricing system for an e-commerce platform. Instead of writing separate functions for every possible sale, you will use currying to create a general discount applicator that can be specialized for different events.
Requirements:
- Create a curried function named
applyDiscount. The first parameter list should take adiscountPercentage(Double), and the second parameter list should take theoriginalPrice(Double). - The function should return the final price after the discount is applied.
- Using
applyDiscount, create two specialized functions:blackFridayDiscount(which applies a 40% discount) andsummerSaleDiscount(which applies a 15% discount). - Test both specialized functions with a product priced at
100.0to ensure they return60.0and85.0respectively.
// Your code here
There are no comments for now.