深入探索单子转换器与自由单子
立即解锁
发布时间: 2025-08-19 00:05:39 订阅数: 3 


Scala 2.13编程实战与进阶
# 深入探索单子转换器与自由单子
## 1. 单子转换器概述
在编程中,我们实现了单子转换器。实现时,我们做了一点小牺牲,将原始函数返回类型的复杂度提升到某种“通用类型”。不过这种牺牲很小,通常只需将原始函数提升到合适的上下文即可。
### 1.1 抽象的钓鱼 API
以下是一个更抽象方式实现的钓鱼组件面向用户的 API:
```scala
abstract class FishingApi[F[_]: Monad] {
val buyBait: String => F[Bait]
val castLine: Bait => F[Line]
val hookFish: Line => F[Fish]
def goFishing(bestBaitForFish: F[String]): F[Fish] = for {
name <- bestBaitForFish
bait <- buyBait(name)
line <- castLine(bait)
fish <- hookFish(line)
} yield fish
}
```
这个 API 对效果类型进行了抽象,为库作者提供了更多灵活性,也为 API 用户提供了更清晰的结构。它可以与任何具有单子的效果一起使用。
### 1.2 不同效果类型的实现示例
#### 1.2.1 Future 和 Optional 混合结果的实现
```scala
import Transformers.OptionTMonad
import ch09.Monad.futureMonad
import scala.concurrent.ExecutionContext.Implicits.global
object Ch10 {
type Bait = String
type Line = String
type Fish = String
}
object Ch10FutureFishing extends FishingApi[OptionT[Future, ?]] with App {
val buyBaitImpl: String => Future[Bait] = Future.successful
val castLineImpl: Bait => Option[Line] = Option.apply
val hookFishImpl: Line => Future[Fish] = Future.successful
override val buyBait: String => OptionT[Future, Bait] =
(name: String) => buyBaitImpl(name).map(Option.apply)
override val castLine: Bait => OptionT[Future, Line] =
castLineImpl.andThen(Future.successful(_))
override val hookFish: Line => OptionT[Future, Fish] =
(line: Line) => hookFishImpl(line).map(Option.apply)
goFishing(Transformers.optionTunit[Future, String]("Crankbait"))
}
```
这里为原始函数实现了外观,只是将它们常规地提升到适当的效果中。`goFishing` 方法可以直接使用,编译器只需 `OptionT[Future]` 的单子实例即可。
#### 1.2.2 返回 Try 类型的实现
```scala
import scala.util._
object Ch10OptionTTryFishing extends FishingApi[OptionT[Try, ?]] with App {
val buyBaitImpl: String => Try[Bait] = Success.apply
val castLineImpl: Bait => Option[Line] = Option.apply
val hookFishImpl: Line => Try[Fish] = Success.apply
override val buyBait: String => OptionT[Try, Bait] =
(name: String) => buyBaitImpl(name).map(Option.apply)
override val castLine: Bait => OptionT[Try, Line] =
castLineImpl.andThen(Try.apply(_))
override val hookFish: Line => OptionT[Try, Fish] =
(line: Line) => hookFishImpl(line).map(Option.apply)
goFishingM(Transformers.optionTunit[Try, String]("Crankbait"))
}
```
当底层函数返回类型从 `Future` 变为 `Try` 时,我们只需改变 `castLine` 函数的提升方式和 `goFishing` 函数初始参数包装器的类型参数,无需修改钓鱼的“业务”逻辑。
### 1.3 单子转换器的作用
单子转换器在某种意义上“扁平化”了两个单子,使得在调用 `map` 和 `flatMap` 方法(以及在 `for` 推导式中)时可以一次性穿透所有层。
### 1.4 Either 类型的实现
```scala
object Ch10EitherTFutureFishing extends FishingApi[EitherT[Future, String, ?]] with App {
val buyBaitImpl: String => Future[Bait] = Future.successful
val castLineImpl: Bait => Either[String, Line] = Right.apply
val hookFishImpl: Line => Future[Fish] = Future.successful
override val buyBait: String => EitherT[Future, String, Bait] =
(name: String) => buyBaitImpl(name).map(l => Right(l): Either[String, Bait])
override val castLine: Bait => EitherT[Future, String, Line] =
castLineImpl.andThen(Future.successful(_))
override val hookFish: Line => EitherT[Future, String, Fish] =
(line: Line) => hookFishImpl(line).map(l => Right(l): Either[String, Fish])
goFishing(Transformers.eitherTunit[Future, String, String]("Crankbait")).value
}
```
这里 `castLineImpl` 的返回类型变为 `Either[String, Line]`,提升过程稍复杂,因为需要向编译器传达 `Either` 左右两侧的类型。
### 1.5 EitherT 类的实现
```scala
implicit class EitherT[F[_]: Monad, L, A](val value: F[Either[L, A]]) {
def compose[B](f: A => EitherT[F, L, B]): EitherT[F, L, B] = {
val result: F[Either[L, B]] = value.flatMap {
case Left(l) => Monad[F].unit(Left[L, B](l))
case Right(a) => f(a).value
}
new EitherT(result)
}
def isRight: F[Boolean] = Monad[F].map(value)(_.isRight)
}
def eitherTunit[F[_]: Monad, L, A](a: => A) = new EitherT[F, L, A](Monad[F].unit(Right(a)))
implicit def EitherTMonad[F[_] : Monad, L]: Monad[EitherT[F, L, ?]] =
new Monad[EitherT[F, L, ?]] {
override def unit[A](a: => A): EitherT[F, L, A] =
```
0
0
复制全文
相关推荐










