柯里化 #

一、柯里化概述 #

1.1 什么是柯里化 #

柯里化(Currying)是将接受多个参数的函数转换为一系列接受单个参数的函数的技术。

1.2 普通函数 vs 柯里化函数 #

普通函数:

scala
def add(a: Int, b: Int): Int = a + b

add(1, 2)

柯里化函数:

scala
def add(a: Int)(b: Int): Int = a + b

add(1)(2)

二、定义柯里化函数 #

2.1 多参数列表语法 #

scala
def multiply(a: Int)(b: Int): Int = a * b

def greet(prefix: String)(name: String): String = s"$prefix, $name!"

def process(data: List[Int])(transform: Int => Int): List[Int] =
  data.map(transform)

2.2 多个参数列表 #

scala
def fold[A, B](list: List[A])(initial: B)(f: (B, A) => B): B =
  list.foldLeft(initial)(f)

fold(List(1, 2, 3))(0)(_ + _)

2.3 调用柯里化函数 #

scala
def add(a: Int)(b: Int): Int = a + b

add(10)(20)

val addTen = add(10) _
addTen(5)

三、部分应用 #

3.1 固定部分参数 #

scala
def multiply(a: Int)(b: Int)(c: Int): Int = a * b * c

val multiplyByTwo = multiply(2) _
multiplyByTwo(3)(4)

val multiplyByTwoAndThree = multiply(2)(3) _
multiplyByTwoAndThree(4)

3.2 创建专用函数 #

scala
def formatNumber(prefix: String)(suffix: String)(number: Int): String =
  s"$prefix$number$suffix"

val formatCurrency = formatNumber("$")("") _
val formatPercent = formatNumber("")("%") _

formatCurrency(100)
formatPercent(75)

3.3 配置函数 #

scala
def connect(host: String)(port: Int)(timeout: Int): Connection =
  ???

val connectLocalhost = connect("localhost") _
val connectLocalhostDefault = connect("localhost")(8080) _

connectLocalhost(8080)(5000)
connectLocalhostDefault(5000)

四、柯里化的优势 #

4.1 参数复用 #

scala
def validate(min: Int)(max: Int)(value: Int): Boolean =
  value >= min && value <= max

val isValidAge = validate(0)(150) _
val isValidPercent = validate(0)(100) _

isValidAge(25)
isValidPercent(105)

4.2 配置与数据分离 #

scala
def process(config: Config)(data: Data): Result =
  ???

val processor = process(defaultConfig) _
val result1 = processor(data1)
val result2 = processor(data2)

4.3 类型推断辅助 #

scala
def eq[A](a: A)(b: A): Boolean = a == b

eq(1)(2)
eq("hello")("world")

4.4 构建 DSL #

scala
object DSL:
  def select(columns: String*): QueryBuilder = ???
  
  class QueryBuilder(columns: Seq[String]):
    def from(table: String): FromBuilder = ???
    
  class FromBuilder(columns: Seq[String], table: String):
    def where(condition: String): WhereBuilder = ???

import DSL._

select("id", "name")
  .from("users")
  .where("age > 18")

五、现有函数柯里化 #

5.1 使用 curried 方法 #

scala
def add(a: Int, b: Int): Int = a + b

val curriedAdd = (add _).curried

val addFive = curriedAdd(5)
addFive(10)

5.2 手动转换 #

scala
def add(a: Int, b: Int): Int = a + b

def addCurried(a: Int)(b: Int): Int = add(a, b)

val addFive = addCurried(5) _

5.3 匿名函数柯里化 #

scala
val add = (a: Int, b: Int) => a + b

val curriedAdd = add.curried

val addFive = curriedAdd(5)
addFive(10)

六、反柯里化 #

6.1 使用 Function.uncurried #

scala
def add(a: Int)(b: Int): Int = a + b

val uncurriedAdd = Function.uncurried(add _)

uncurriedAdd(5, 10)

6.2 手动转换 #

scala
def add(a: Int)(b: Int): Int = a + b

def addUncurried(a: Int, b: Int): Int = add(a)(b)

七、柯里化与高阶函数 #

7.1 柯里化 map/filter #

scala
def map[A, B](f: A => B)(list: List[A]): List[B] =
  list.map(f)

def filter[A](p: A => Boolean)(list: List[A]): List[A] =
  list.filter(p)

val doubleAll = map((_: Int) * 2) _
val filterEvens = filter((_: Int) % 2 == 0) _

doubleAll(List(1, 2, 3))
filterEvens(List(1, 2, 3, 4, 5))

7.2 柯里化 fold #

scala
def foldLeft[A, B](initial: B)(f: (B, A) => B)(list: List[A]): B =
  list.foldLeft(initial)(f)

val sum = foldLeft(0)(_ + _) _
val product = foldLeft(1)(_ * _) _

sum(List(1, 2, 3, 4, 5))
product(List(1, 2, 3, 4, 5))

7.3 函数组合 #

scala
def compose[A, B, C](f: B => C)(g: A => B): A => C =
  x => f(g(x))

val double = (x: Int) => x * 2
val addOne = (x: Int) => x + 1

val doubleThenAddOne = compose(addOne)(double)
val addOneThenDouble = compose(double)(addOne)

doubleThenAddOne(5)
addOneThenDouble(5)

八、实战示例 #

8.1 HTTP 客户端 #

scala
def httpRequest(method: String)(url: String)(body: Option[String]): Response =
  ???

val get = httpRequest("GET") _
val post = httpRequest("POST") _

val getUser = get("/users/1") _
val createUser = post("/users") _

getUser(None)
createUser(Some("""{"name": "Alice"}"""))

8.2 数据验证 #

scala
def validate[T](field: String)(rules: (T => Boolean, String)*)(value: T): Either[List[String], T] =
  val errors = rules.collect {
    case (rule, message) if !rule(value) => message
  }
  if errors.isEmpty then Right(value)
  else Left(errors.map(e => s"$field: $e"))

val validateAge = validate("age")(
  (_: Int) > 0 -> "must be positive",
  (_: Int) < 150 -> "must be less than 150"
) _

validateAge(25)
validateAge(-5)
validateAge(200)

8.3 构建器模式 #

scala
case class Query(
  table: String,
  columns: List[String] = List("*"),
  where: Option[String] = None,
  limit: Option[Int] = None
)

def query(table: String): Query = Query(table)

def select(columns: String*)(q: Query): Query =
  q.copy(columns = columns.toList)

def where(condition: String)(q: Query): Query =
  q.copy(where = Some(condition))

def limit(n: Int)(q: Query): Query =
  q.copy(limit = Some(n))

val buildQuery = query("users")
  .pipe(select("id", "name"))
  .pipe(where("age > 18"))
  .pipe(limit(10))

九、柯里化与部分应用的区别 #

9.1 柯里化 #

scala
def add(a: Int)(b: Int): Int = a + b

val addFive = add(5) _
addFive(10)

9.2 部分应用 #

scala
def add(a: Int, b: Int): Int = a + b

val addFive = add(5, _: Int)
addFive(10)

9.3 对比 #

特性 柯里化 部分应用
定义 多参数列表 单参数列表
调用 f(a)(b) f(a, b)
部分应用 f(a) _ f(a, _: B)
类型推断 更好 一般

十、总结 #

柯里化要点 #

  1. 将多参数函数转换为单参数函数链
  2. 支持部分应用和参数复用
  3. 有助于类型推断
  4. 适合构建 DSL

使用场景 #

场景 示例
参数复用 validate(min)(max)(value)
配置分离 process(config)(data)
类型推断 eqA(b)
DSL 构建 select().from().where()

下一步,让我们学习 偏函数

最后更新:2026-03-27