Scala3:如何用类型Lambda重构带上下文绑定的MonadWriter?
Scala 3类型Lambda重构MonadWriter的正确方式
原可运行的Scala 2代码
trait Monoid[A] { def zero: A def append(a1: A, a2: A): A } trait Monad[F[_]] { def unit[A](a: A): F[A] def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] def map[A, B](fa: F[A])(f: A => B): F[B] = { flatMap(fa)(a => unit(f(a))) } } given monadWriter[W: Monoid]: Monad[({type l[x] = Writer[x, W]})#l] = new Monad[({type l[x] = Writer[x, W]})#l] { override def unit[A](a: A): Writer[A, W] = Writer(a, implicitly[Monoid[W]].zero) override def flatMap[A, B](fa: Writer[A, W])(f: A => Writer[B, W]): Writer[B, W] = { val fa2 = f(fa.v) Writer(fa2.v, implicitly[Monoid[W]].append(fa.w, fa2.w)) } }
编译失败的Scala 3尝试代码
given monadWriter[A, W: Monoid]: Monad[[W]=>Writer[A,W]] = new Monad[[W]=>Writer[A,W]] { override def unit[A](a: A): Writer[A, W] = Writer(a, implicitly[Monoid[W]].zero) override def flatMap[A, B](fa: Writer[A, W])(f: A => Writer[B, W]): Writer[B, W] = { val fa2 = f(fa.v) Writer(fa2.v, implicitly[Monoid[W]].append(fa.w, fa2.w)) } }
错误原因分析
- 类型参数冲突:给定实例的类型参数
W与类型Lambda内部的参数W重名,编译器无法区分固定的日志类型W和类型Lambda需要的值类型参数。 - 多余的类型参数:
monadWriter不需要额外的A类型参数,Monad[F[_]]要求F是接受单个类型参数的构造器,W作为固定的日志类型,应是给定实例的唯一类型参数。 - 方法内类型混淆:尝试代码中方法的
A参数与给定实例的A参数重名,进一步引发类型解析混乱。
正确的Scala 3重构代码
given monadWriter[W: Monoid]: Monad[[X] => Writer[X, W]] = new Monad[[X] => Writer[X, W]] { override def unit[A](a: A): Writer[A, W] = Writer(a, summon[Monoid[W]].zero) override def flatMap[A, B](fa: Writer[A, W])(f: A => Writer[B, W]): Writer[B, W] = { val fa2 = f(fa.v) Writer(fa2.v, summon[Monoid[W]].append(fa.w, fa2.w)) } }
关键修改点
- 类型Lambda参数重命名:将类型Lambda内的参数从
W改为X,避免与日志类型参数W冲突,明确X代表Writer的值类型。 - 移除多余的
A类型参数:给定实例仅保留W: Monoid作为类型参数,符合Monad[F[_]]对类型构造器的要求。 - 替换隐式获取方式:用Scala 3推荐的
summon替代implicitly,语法更简洁清晰。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

