PDF版 ePub版

# 柯里化和部分函数应用

## 部分应用的函数

Email 类看起来仍然是这样：

case class Email(
subject: String,
text: String,
sender: String,
recipient: String)
type EmailFilter = Email => Boolean

    type IntPairPred = (Int, Int) => Boolean
def sizeConstraint(pred: IntPairPred, n: Int, email: Email) =
pred(email.text.size, n)

    val gt: IntPairPred = _ > _
val ge: IntPairPred = _ >= _
val lt: IntPairPred = _ < _
val le: IntPairPred = _ <= _
val eq: IntPairPred = _ == _

    val minimumSize: (Int, Email) => Boolean = sizeConstraint(ge, _: Int, _: Email)
val maximumSize: (Int, Email) => Boolean = sizeConstraint(le, _: Int, _: Email)

    val constr20: (IntPairPred, Email) => Boolean =
sizeConstraint(_: IntPairPred, 20, _: Email)

val constr30: (IntPairPred, Email) => Boolean =
sizeConstraint(_: IntPairPred, 30, _: Email)

### 从方法到函数对象

    val sizeConstraintFn: (IntPairPred, Int, Email) => Boolean = sizeConstraint _

## 更有趣的函数

    def sizeConstraint(pred: IntPairPred)(n: Int)(email: Email): Boolean =
pred(email.text.size, n)

    val sizeConstraintFn: IntPairPred => Int => Email => Boolean = sizeConstraint _

sizeConstraintFn 接受一个 IntPairPred ，返回一个函数，这个函数又接受 Int 类型的参数，返回另一个函数，最终的这个函数接受一个 Email ，返回布尔值。

    val minSize: Int => Email => Boolean = sizeConstraint(ge)
val maxSize: Int => Email => Boolean = sizeConstraint(le)

    val min20: Email => Boolean = minSize(20)
val max20: Email => Boolean = maxSize(20)

    val min20: Email => Boolean = sizeConstraintFn(ge)(20)
val max20: Email => Boolean = sizeConstraintFn(le)(20)

### 函数柯里化

    val sum: (Int, Int) => Int = _ + _
val sumCurried: Int => Int => Int = sum.curried

### 函数化的依赖注入

    case class User(name: String)
trait EmailRepository {
def getMails(user: User, unread: Boolean): Seq[Email]
}
trait FilterRepository {
def getEmailFilter(user: User): EmailFilter
}
trait MailboxService {
def getNewMails(emailRepo: EmailRepository)(filterRepo: FilterRepository)(user: User) =
emailRepo.getMails(user, true).filter(filterRepo.getEmailFilter(user))
val newMails: User => Seq[Email]
}

MailboxService 实现了这个方法，留空了字段 newMails，这个字段的类型是一个函数： User => Seq[Email]，依赖于 MailboxService 的组件会调用这个函数。

    object MockEmailRepository extends EmailRepository {
def getMails(user: User, unread: Boolean): Seq[Email] = Nil
}
object MockFilterRepository extends FilterRepository {
def getEmailFilter(user: User): EmailFilter = _ => true
}
object MailboxServiceWithMockDeps extends MailboxService {
val newMails: (User) => Seq[Email] =
getNewMails(MockEmailRepository)(MockFilterRepository) _
}